As more and more companies try to become software companies, being a good developer is a highly sought-after skill in the market. However, I find that most new graduates and even many developers with years of experience still lack some basic skills that can transform them from an average developer into a great developer. Most of these are from my own experience over the last decade in building software, teams and deploying software in production.
Most articles in this area recommend things like
1) Do more practice
2) Solve coding puzzles
3) Read open source code and other solutions – all of which are reasonable but not sufficient. In fact, I would argue that these things by themselves don’t help much at all. There are five other key ingredients that separate the great developers from the rest.
1) Understand How Memory Allocation and Free Works
One of the key jobs of any programming language is to provide a memory model to the programmer to deal with allocation and deallocation of memory objects. This also includes passing objects to a function by reference or by value, knowing when the object change in function will impact the original object, and when it is done to a copy. Poor understanding of the memory model is one of main root causes of bugs in a code base. As a developer, you should be able to answer three questions about any object in your system:
- Where is the memory coming from – stack or heap?
- As you pass around the object, is the memory getting replicated or am I using a reference to the same object?
- When will memory get freed, and who will free it?
If you cannot answer any of these questions about an object you use, there is a high chance you will create a bug sooner or later in the code.
2) Develop system debugging skills
In most cases, whether you are writing the first version of code or trying to improve an existing version, the key skill is being able to root cause a problem and fix it. Most developers who have learned basic coding but do not understand the overall system concepts like locking, threads, storage, networking, and operating system APIs will look for quick hacks to fix the symptom of a problem. In most cases the fix will just check for the failed scenario and handle that, or add a bigger timeout, or add a sleep to wait for an operation to finish. Correctly written code should not depend on the speed of the underlying hardware. Being good at debugging is a skill that most people ignore and it is needed even more now. With the advent of microservices, code running across various machines and talking to each other using http or RPC calls, API-based development, lack of in-memory locks and unpredictable network delays, it has become even more critical to know how systems work and how networking and storage can impact your code behavior. Unfortunately, most of the coding exercises and websites focus on puzzles and not on these real system issues.
In many interviews, I find that developers with very good scores on programming and data structures often fail on basic things like how to get the IP address of a machine, how to check if a service on remote machine is available or even how to get network latency to a remote machine, given its IP address.
3) Check and Handle Every ERROR That’s possible
I find it very common that developers call a function that can return an error and ignore that error. There is a reason a function returns an error: “IT CAN HAPPEN.” This means that you should definitely handle the error and figure out what can be done about it. Either the caller should handle the error or pass it on to the higher layer. At some point, the main operation might fail because some function at the lower level returned an error. If you are skipping an error check, there had better be a very good explanation for it and it should be made clear in the code, so that any other developer does not have to wonder what happened to error-handling.
In fact, in most production quality code, I think 50 percent of the code is dedicated to error-handling. That is because most system calls, library calls and remote calls can return an error or timeout. This is again one of those things that is not developed by doing coding puzzles or practicing small code samples that do not make any real system calls like opening a file, writing to a network socket and so forth. Developers write practice programs mainly to deal with simple data structures and solve puzzles that are rarely present in real code. Real code is much more boring than those puzzles!!
4) Understand the Performance of Data Storage Layers
In any computer system, there is a hierarchy of storage banks that can store any piece of data for your program. Knowing the basic performance characteristics can help you make decisions about what to keep in which layer. A basic hierarchy includes:
- L1, L2, L3 caches
- Main memory
As a developer, you should know how long it takes to get data from each of these layers, so that you can decide when to store data in main memory and when to always read it from the underlying storage media. For example, it takes about 50-80 ns to read data from main memory, 20-50 microseconds to read from an SSD, and 5-10ms to read from a disk. It can severely impact your performance if you start reading data from a disk instead of keeping it in main memory. In general, a good rule of thumb is to avoid doing any disk IOs in critical path unless it is absolutely necessary. There is a reason most good software modules try to buffer data in main memory and do bulk IOs to disk in the background. In cases when that is not possible, one can do a write to a commit log and then write to the actual location in a batch. There are other techniques also, but that is a topic for another blog or even a book!
Overall, make sure that you know when to keep data in main memory and when to store it to disk. Also know that something just sitting in main memory can get lost during a system reboot or crash. So before you commit to any external process where the data is stored, make sure it is stored in some persistent storage.
5) Take Ownership and Understand the Bigger Picture
This last one is around personal discipline and helping with the overall process of building great software. This is something everyone should be able to do without requiring a lot of new training or a new skill set. Software development has inherent risk in terms of timeline as things can take longer than expected. How many times have you told your friends or family that you are almost done with the issue at hand and will be back in an hour, and it has taken more than six hours to get the problem resolved. I have missed many dinners because of this!
In any software system, the team or the company is trying to achieve a bigger goal. Most commonly, this is releasing a product on a certain date with specific features. But some other cases can be fixing an issue for a specific customer and a specific scenario or creating an internal library or software to be used by other teams.
In any of these cases, the company is using tools to manage codebase like git, perforce, or SVN and to manage bug tracking like JIRA, Bugzilla or Asana. Typically, there are dedicated people who are managing a release, features within a release and tracking work from people in a team. A developer who is aware of the overall plan and who tracks his or her progress relative to the release timeline can significantly reduce the overall risk for the product.
Some of the behaviors for great developers include keeping his or her bugs updated all the time, doing a better estimation of time needed for work, focusing on getting the work done instead of getting distracted into solving unrelated problems, letting the other team members and manager know when a timeline is at risk, and testing their own feature for various inputs and trying to break it before it goes to QA. In fact, if most developers could follow these, we might not need as many managers as we have today in most companies. We may also get rid of the sand-bagging that most managers have to do in order to make a release on time. Even with all that, software never ships on time with all the expected features and quality. You have to pick two out of these three!
I really hope that developers and evaluators will reduce the focus on solving coding puzzles and instead focus on understanding the overall computer system where the code runs, how software modules interact over a network, the importance of error handling and how the software development process works in the real world.
For any comments or suggestions, feel free to reach out to me at firstname.lastname@example.org
Or @ajaygulati on twitter