How To Write Great Code — 16 Ideas | by Jonas Neumann | Nov, 2022

Getting There Techniques and Principles

photo by Adi Goldstein Feather unsplash

My answer to this question keeps on evolving.

Today I was asked to review someone’s codebase. Before diving into the code I thought I should explain what is the standard against which I was going to measure the code.

There are principles and techniques that I find useful. I think it’s worth sharing them and getting feedback and perspectives from the wider community.

There are two parts to this equation. The first is the code itself (great code) and the second is the process of coding (how to write it).

1. Respect what came first and be flexible in approach

These are two principles from the Amazon Principal Engineering Community that resonate with me.

As humans, we are not good at agreeing on things (Android vs iOS, Cats vs Dogs). Therefore, it is unlikely that your team will share all your ideas.
You are unlikely to write great code if you are not flexible and do not value the work of others.

2. Adopt Multiple Patterns

It’s not object-oriented programming vs functional programming, use both! Polymorphism and object inference are just as useful as pure functions and immutability.

There is also a place for procedural programming. Even in Haskell, side effects are done in a procedural style. Sometimes you want to control the flow step by step.

3. Keep it simple. Your code is read by humans.

Simple code is easy to read but hard to write. In my current project, we use AWS Lambda functions. If I don’t understand what the function does after 15 seconds, that’s not very good code.

Simple code will go a long way to make maintenance manageable. You don’t want to get into a situation where you end up rewriting the entire module when you need to make a small change.

4. Functions and classes are intended to be mostly abstract rather than reusable

If I have 50 lines of code in my lambda handler, I can’t figure it out in 15 seconds. I can achieve this only if I use abstraction.

Some people follow this principle only to write reusable functions. Reusability is great but it usually means we have to generalize. Being clear often helps to make code more understandable. I think a balance needs to be struck.

5. Naming is difficult. use domain language

I often find that domain experts usually have a name if I have trouble finding a name for something in my code. And if they don’t then I am making a mistake.

Naming your functions and data structures the same way as a domain expert makes it easy to translate your requirements into tests as well.

6. Great Code Does What It Should

Take the time to understand the problem you are trying to solve. Understanding the domain and using that language will go a long way in ensuring that you are solving the right problem the right way the first time around. We delve into the design and use the first solution that comes to mind.

7. Co-find Infrastructure, Configuration, Pipeline, and Application Code

The different layers of an application should be available to its developers. Not every developer needs to touch all the code, but for great integration, the code should be accessible to everyone on the team.

Just as there shouldn’t be a “wall of confusion” between teams, there shouldn’t be a “wall of confusion” in your code either.

8. Separate the domain and business logic from the rest of the code

There are many ways to structure your code. There is to be discussion about whether to structure by type/layer or attribute or various combinations of both. To be honest, I don’t care much.

One thing I find important is to keep domain and business logic separate from side effects and configuration code. When implementing my domain logic, I want to worry as little as possible about the database, HTTP calls, threads, framework configuration, etc.

I’m not saying that that code is less important. I am saying that all these side effects need to be handled with care. This is where stuff can break and I need to use try-catch or monads and data validation (null-checks, etc.). If my side-effects and business logic are separate, I can deal with the complexities of each separately and thus keep my code simple.

9. Balancing Quality, Time and Cost

Quality has its price. The best quality code that cannot keep within the constraints of time and cost is useless code. Great code strikes a good balance between quality, time and cost. Balance usually means tension. Quality is something intangible and a bit difficult to measure so it is difficult to give it due importance in this balance.

When someone is asking to make something of lesser quality just to get it faster, I would be very careful. As we’ll explore further, quality is made up of several factors, each of which has the potential to render your software unusable. So, when it comes to downplaying the quality, it doesn’t have much room to play.

10. Quality is a balance of performance, flexibility, safety, maintainability, observability and reliability

It’s a mouthful, I know. But if you think about it, we all need good quality of these. Without performance, our users may stop using our software, it equates to flexibility and reliability. Bad security can have all kinds of implications, financial, legal, and more.

Without observability, our users are the ones who find all of our bugs and we have no way of confirming whether our fixes have resolved their issues. Maintainability will allow our software to change easily over time. If we don’t have it, the software will be rewritten or eventually thrown away.

It is clear that we need all of these aspects, but we also need to remember to find a balance between them. As we have to live within budget and time constraints, we cannot focus on performance first and safety and maintenance for example if we have time. When it comes to performance, I would recommend using caching, concurrent and parallel computing only if we are certain they are necessary. These advanced display techniques can potentially damage all other aspects of quality.

I realize I’m only scratching the surface, if you’d like me to elaborate on any of these aspects, let me know.

We’ve covered what good code looks like, the other important part is how to get there. There are many things we can do to help with this process.

11. Red Green Refactor

TDD in 3 words. I won’t go into the discussion of whether you should write tests first, or what unit tests are, etc. I find that at a high level, a discipline that implements TDD which is very helpful is that you need to understand your requirement well enough to write a test that proves that you completed it. Is. (Some would call this acceptance test driven development).

Why spend 10 minutes asking questions, if you can spend 3 days developing the wrong thing? ,

12. The number one enemy of productivity and flow is unplanned work.

I don’t like writing documentation, I don’t. But unplanned work is so bad, I get used to writing down requirements and explanations and only start coding when I’ve done it.

My time is so precious that I can’t get into the chaotic rush of misunderstandings that could have been avoided if I had taken the time to write them down.

If you can, agree on a definition of ready for your stories and stick to it. If your team isn’t on board, do it for yourself anyway. OK, so once I know what my test should look like, I can write the test and pass it. Then, instead of thinking you’re done, reread your code. You’ll know if it needs to be refactored or not.

13. Refactor Now, You Won’t Do It Later

Yes, some teams have an error budget and schedule to refactor, but I don’t think it’s a good idea. You have to refactor the context switch to code that probably isn’t in good shape because you knew you were going to refactor later.

Instead, refactor directly after passing the test. You have all the references you need. You will also know whether your test was good or not. If you’ve tested implementation details rather than behavior, you’ll need to change your testing.

14. Don’t Hurry

If we take a look at all the points I’ve mentioned about what makes great code, it’s clear that it’s not easy to write. You will not get there without meditation and uninterrupted concentration. For me, I don’t think I can’t even write good code when I’m running. What do you do when you realize you can’t meet the deadline or any time commitment?

My instinctive reaction would be to rush to finish it and thus cut corners. This is the wrong choice! Let me explain

Compromising on quality can be a legitimate business decision. The key word here is business. If we are faced with a situation where we cannot stick to the plan, then we need to change the plan. If you choose to hurry and cut corners, you are making a business decision that probably isn’t yours.

We need to realize as developers that the plans we work against are very brittle. They are based on assumptions, guesses and feelings. We are not bad developers if we can’t complete a plan. However, we are bad developers if we know we need to change the plan and don’t communicate it.

15. Improvement in daily work is more important than daily work itself

It’s important to consider and improve your workflows and tools. Don’t ask for time to do this, just do it.
Automating your linters, test runners, IDEs, auto-completion, and manual tasks will speed you up and allow you to focus on development.

16. Plan ~4 hours/day in the IDE

As a developer, we need to realize that we don’t spend whole day in development. A significant amount of money is spent on understanding the domain and requirements, designing the architecture, explanations and presenting the outputs and results, as well as on documentation.

I can’t remember the source, but I read that developers are happiest when you spend at least 4 hours per day in your IDE. This may vary depending on your role and seniority.

However, you should know that number and adjust your estimates accordingly. If you estimate that a certain story takes you 2 days to develop, you need to estimate 4. Because for 2 days of development you need 4 hours/day of development. (The same concept applies to story points) I would also recommend sharing this number with your PO/BA for transparency.

As developers, in my view, we should spend on average 3 to 6 hours a day in our IDE during a project. Anything less than that and we’ll have trouble making a real contribution. Anything more than that we are potentially not communicating enough and risking misunderstanding requirements or poor design and collaboration.

Leave a Reply