𓃰 The Diligent Developer

The Diligent Developer Framework

31 Jan, 2024

What makes a great programmer, and how can you become one? This is an important question that many authors have tried to answer.

On one hand, we have great books like The Pragmatic Programmer, or A Philosophy of Software Design, which address this question heads on. They will make an experienced software developer nod in agreement from cover to cover, but they won't resonate as much with a junior developer.

On the other hand, there is no shortage of courses and tutorials on specific programming languages and frameworks. But after following some of these tutorials, you may still find that something is missing and be lost on how to continue improving.

The Diligent Developer framework is an attempt to provide practical guidelines to assess and improve your skills to become a better programmer.

The 6 skills

I have divided the skills that a developer should master into 6 categories:

  1. Coding & problem-solving
  2. System design and architecture
  3. Programming language
  4. Third-party components
  5. Communication
  6. Environment

Before going deeper on what each of these skills mean, let me explain the reasoning behind this list. The list is comprehensive and orthogonal: mastering all 6 skills will make you a great programmer, but being good at any of these skills doesn't automatically make you good at the others.

I have seen great problem solvers limited by their lack of understanding of the programming language they where using, I've seen proficient programmers unable to use a relational database to their advantage. It's common for great developers overall to end up working on the wrong problem due to some miscommunication with managers or colleagues.

Let's go deeper on each of these skills.

1. Coding & problem-solving

Coding is the translation of your thoughts into code, and the related tasks that that requires. It includes writing new code, fixing a compilation error, debugging an issue, writing a unit test. Coding is what you do with a problem that you can fit in your head, when you focus on a specific task.

Some people may think that coding is not that important, that it is the kind of thing junior developers do, while senior ones focus on system architecture and other high-level topics.

Quite the opposite, you need to be a great coder in order to become a great software engineer, and framing coding and architecture as competing skills is completely misguided. There are plenty of great coders that have lead big projects and organizations without compromising on their coding skills, e.g. Peter Norvig, George Hotz or DHH.

Others might argue the opposite, that coding is all there is to software development, and the rest is just fluff. This may be the case for extremely talented developers who take for granted a bunch of other skills, but rarely constitutes a good piece of advice for the rest of us.

The best way to become a better coder is to deliberately practice on problems of the right difficulty: neither too hard nor too easy. I find the Advent of Code to be a great exercise, but to make the most of it, you need to become aware of your process. Iterate on your code to make it as simple as possible, think about what took you so long, why a given bug was difficult to debug, why some program you wrote worked flawlessly and another required a lot of back and forth, and compare your solution with others.

The good thing about improving your coding and problem-solving skills is that solutions that you come up with will be simpler, which will make those problems more manageable, and will allow you to tackle bigger problems.

2. System design and architecture

Coding and problem-solving can only get you so far. Eventually, you will face a problem that doesn't fit in your head, and you will need to break it down into smaller problems.

At a small scale, this is what you do when you write a function or a class and choose its parameters, attributes, and the way they interact.

At larger scales, you need to think about larger components, how they are implemented, how they communicate, how they are deployed, how they are monitored, by whom they are developed.

A bad design introduces unnecessary complexity and can have all kinds of deleterious effects: it can affect performance by adding bottlenecks, make the system error-prone by making it difficult to test, slow development by making the setup of the development environment difficult.

We developers have a tendency to tackle imaginary problems. When thinking about software architecture, we tend to think about the future: scalability, high availability, teams of developers.

However, a good architecture should be as simple as possible to get the job done, and should leave room for future changes.

To become a better system designer, there are two things you need to do. First, you need to think deeply of the implications of your design decisions. When there is a bug, an outage, when a developer is blocked, you need to think hard about how your design contributed to that outcome.

Second, you need to learn from others' experience. Blog posts are best in my opinion. Read why someone thinks microservices are stupid, and how someone runs a company on 3,000 microservices, how someone deploys 10 times a day to production, why someone stopped using X because it was unreliable. With time, you will start to see patterns, and you will be able to make better decisions.

3. Programming language

Whenever you are coding, you are using a programming language. And which one you use is a big deal.

An argument could be made that it's not important which programming language you use. Twitter was built on Ruby, Instagram on Python, WhatsApp on Erlang, so... does it really matter what programming language you use? It seems you can accomplish mostly anything with any programming language. Why not just pick one and be done with it?

The truth is that the programming language you use does matter. WhatsApp chose Erlang because it's designed to be fault-tolerant and distributed, Twitter and Instagram were built on Ruby on Rails and Django, the web frameworks that revolutionized web development in the late 2000s.

A programming language is not only its syntax, it's the ecosystem and the community. It's the libraries, the frameworks, the tools, the conventions. As stated almost 20 years ago by Paul Graham in The Python Paradox, a programming language should get you excited by itself.

There are two paths to improve on this skill: learn more about the programming language you are using, and learn more programming languages.

It's difficult to overstate how deep you can go into a programming language. Mastering Python, JavaScript, Java, or any other language, is a life-long journey. You may start learning the syntax, then there's the standard library and third party libraries. You can go a long way with just this knowledge. But then there are the conventions, the right way to do things, the idioms, the advanced features: concurrency, generics, metaprogramming; and then there's the internals: the implementation of the language, the compiler, the virtual machine, the garbage collector.

Some developers are deeply bonded to one programming language. They learned to code with it, grew to think in that language, and it makes them very productive. Sometimes, this attachment is not so healthy, and thinking of trying new languages fosters fears of being unproductive, feeling dumb, or wasting time.

It's important not to confuse learning a programming language with learning to program, which generally go hand in hand the first time. Most of the coding skill is transferable from one language to another, and learning new programming paradigms can be a low-hanging fruit for improving your craft.

4. Third-party components

We love to code, but we can't code everything (there are some exceptions).

Databases, caches or message queues, and libraries that implement encryption, compression, graphics are clear contenders for not being coded from scratch.

Knowing how to use a relational database or a message queue is hugely beneficial for you as a developer. For one, it removes a ton of responsibility from you. In a hundred years, I couldn't develop an equivalent of PostgreSQL, so it is great that it exists and I can use it for free.

Apart from filling a concrete need in our system, third-party components bring other benefits. Being exposed to great tools that solve difficult problems also serves as an example to follow in your own coding. This is specially true for open source tools.

Good and sane decisions in naming, API design, backwards-compatibility, etc. can serve as an inspiration for your own work.

But there's a world of difference between knowing that a tool exists, and knowing how to use it. Phrases like "just put a cache in front of that service" or "just store it in a database" are often thrown around, but properly implementing them is much harder.

Even though each tool is different, I've also found that learning how to use them is also a transferable skill. Learning to test and benchmark different configurations, navigating the documentation, finding help online, prototyping before going all in are things that you can practice.

Similar to third-party components, we have services. Sentry, Datadog, AWS, Mailgun, Stripe are undoubtedly useful services, but they are also black boxes. They come with great benefits, but the skill of using them is not that transferable and doesn't contribute as much to making you a better developer.

If you are used to relying on managed databases or clouds, I suggest you install some of those components locally, and play around with them. Redis, PostgreSQL, ClickHouse, SQLite, NATS are all great and inspiring projects.

5. Communication

When you code, you are communicating with the machine, but you are also communicating with other developers. You communicate through your code, with your choice of names for variables, functions, classes, and also through your comments.

Apart from the code, you also communicate through commit messages, bug reports, issue comments, instant messages and emails. And beyond written communication, you surely have conversations, meetings or presentations.

Being effective at these forms of communication is crucial to be a good developer. It's not uncommon for great developers to be outstanding bloggers, science fiction writers or best-selling authors. Counter to popular belief, good programming skills correlate more with good communication skills than with math talent (see The Programmer's Brain).

A crucial side effect of improving your communication skills is that of improving your thinking. Has it ever happened to you that in the process of writing a detailed StackOverflow question or a question to some colleague you found the solution to the issue yourself? It's not a coincidence. By making the effort to explain the issue to someone else, you are forced to get a clearer understanding of the problem, and this often leads to the solution.

Similarly, by splitting each commit message into logical chunks, with a description of the changes introduced, you can get a better understanding of what you are doing, and you can also help your colleagues understand your code.

Practice is crucial to become a better communicator, and there's a lot you can do. Trying to write the perfect email, documentation page or commit message can be blocking and frustrating. It's better to practice and try to improve gradually, getting out of your comfort zone bit by bit. Explain the last bug you fixed to your hairdresser, write a blog post about this thought you had, spend 2 more minutes on that commit message.

Communication plays a central role in your work as a software engineer and it requires you to be conscientious just like for any other skill.

6. Environment

Programming is one of the most purely intellectual activities you can do, but it doesn't happen in a void. Mastering the tools around your craft can greatly improve your productivity.

This environment includes your code editor or IDE, the debugger, shell, version control software, password manager, to-do app, operating system and any other tool you use to get your job done. It also includes your physical setting: keyboard, mouse, screen, desk, chair, and the room you are in. You could even fit your diet, exercise and sleep habits in this category if you wish.

All of these things can make a big difference in your productivity, specially for the worse. You will not get a huge productivity boost by upgrading your keyboard or getting a comfortable chair, but you will get a huge productivity hit if you develop RSI or back pain.

Tweaking and improving your environment is the topic on this framework that is more prone to fetishization. It can be exciting to build your own keyboards and learn a new layout every 3 months, but you must not kid yourself into thinking that this will make you a better developer.

Still, there are some things that are worth investing time in. Same as with coding, you need to be aware of your process and realize when your environment is the bottleneck.

Conclusion

We developers take hundreds or thousands of decisions every day, big or small. I hope this framework allows you to zoom out and think of your craft as a whole, hopefully providing some guidance in your journey.

Where to go from here

  • Ping me on X and let me know if you found this useful or are missing something, I'd love to hear your thoughts.

  • Check out Python.cards, a site I'm building to learn Python with spaced repetition.

  • I'm preparing a cohort-based software design course organized as a "writing class", in which students write a medium-sized project (vi-like editor) and iteratively improve the code via code reviews and live discussions. If that sounds interesting, please write to me at [email protected].

Finally, I write a newsletter in which I share my thoughts on software development, productivity, entrepreneurship and more. Subscribe below if you want to receive it.