At its core, Software Engineering has to do with crafting and building things. We are all craftsmen. It’s about converting ideas we have in our minds into creating things, whether that means a website, a mobile app, or some small one-off script.
Each craft has a set of principles that make you better when you follow them. Especially when you work in a team, these principles help you set the right engineering team culture.
Why software engineering teams need principles
Software engineering principles are guidelines that help software development teams be better at their craft. The importance of principles is often overlooked in software engineering teams.
I want to be very clear that I’m not suggesting that you should adopt all of these principles for your engineering team. But they will serve as a baseline from which you can begin to think about ways to improve your own engineering culture.
Following such principles help your team members to work together as one cohesive unit – align them all towards one common goal.
Principles also make sure you follow the best practices and make your team effective. And eventually, your team members can level up their skills faster.
Here are some principles I’ve learned about building and running a great software engineering team. Whether you’re a manager or a developer, you can apply these principles in your next project and beyond.
1. Keep It Simple, Stupid
Most software systems are complex by nature. Many times, we accidentally introduce additional complexity into our code. If your software becomes too complex to understand and debug, can you even trust it?
I would say that the most successful software engineers are the ones that keep their code simple, consistent, and easy to understand. Good software engineers know that if you make something overly complicated, the quality of the code suffers.
In order to achieve excellence in software engineering, it is crucial to understand how to simplify things. It is essential that our code & solutions be straightforward and simple to understand.
Good software should be simple enough that you can keep the entire model of it in your mind. Complexity often leads to poor-quality code, or worse: bugs. These bugs can lead to user error or even a security hole. It’s important to avoid these problems at all costs.
2. Build Incrementally & Iteratively
I think this is one of the most important points to remember when building a successful software application.
I hear you asking “What’s the difference between iterative and incremental?”
In short: Iterative means developing a system through repeated cycles getting feedback at each stage and incremental means through small chunks at a time. You can go ahead and read the Wikipedia article for more detail.
It is true that you need to build incrementally. However, it is also important to build iteratively. You need to get feedback. You need to listen to what your customers are telling you and take action accordingly. Constantly adjust based on the feedback.
Software engineers who build incrementally and iteratively over time are better at their jobs. In fact, the ability to start building incrementally and iteratively early on in the design process is one of the main reasons why some engineering teams are able to ship products faster than others.
3. Tolerate Ambiguity. Be Agile.
Software development involves many ambiguities and uncertainties. No one knows what the customer wants or would pay for.
Being agile allows you to deal with ambiguity better. When faced with ambiguity, rather than trying to decide what is right or wrong, the point is to find a solution that works for you in that particular context.
There is no such thing as a perfect program. Any code can be improved. Ambiguities are a part of life, and any professional who wants to succeed in software development must tolerate them.
4. Fix problems, even when they are not created by you
As a software engineer, you need to be able to fix things even if they are not created by you. As junior developers, you might feel that you aren’t experienced enough to fix a bug in a long-running project. But even if you could take up one bug and fix it, your team would appreciate you.
Fixing problems is how you develop your problem-solving skills. The process of fixing the bug would lead you down a path that would allow you to gain valuable insights into the product and why something was built the way it was.
5. Don’t reinvent the wheel
Many software teams fall for the Not Invented Here Syndrome. You would feel that you could build your own framework or library and do a much better job than that open-source library. Some despise proprietary software that they wouldn’t even think of using paid software or libraries.
Your job is to build products that solve business problems. If there is an existing tool that does the job 80% of the time, use it first. Stop trying to reinvent everything from scratch. Your customers don’t care if you spent 6 months perfecting the right framework. As long as their problems are solved, they don’t care.
6. Keep variations in technologies to a minimum
When working with a team, having the same set of tools/frameworks/libraries/OS is really important. You want to onboard new developers to the team quickly. Freezing on the libraries and tools (and even the versions) is very important.
Yes, as developers we love to tinker around with the latest new programming language or technologies. But unless it is 2x or 3x’es productivity and speed, I wouldn’t even bother. Also, it’s easier to find new developers if you stick to the popular frameworks.
7. Ask in the open. Learn from everyone.
When you are stuck with a problem, quickly ping the dev team channel asking if someone can help. I love async communication, but I do love to post whenever I am stuck.
The process of articulating my problem allows me to think about the problem from a different angle and that itself would help me solve it. Check out Rubber Duck Debugging. Even if I can’t solve it myself, someone else would have faced it and would help me.
The second point is equally important. You should try to learn from everyone. Be humble and you will get a chance to learn something new every day from someone. If you think you are too big to listen to others, you will surely fall.
8. Admit your errors also in the open. Quickly.
This is a corollary to the previous point. Just like you want to ask your questions in the open, admit your errors and mistakes out in the open. Make sure you explain what happened and the steps you have taken to ensure it won’t be repeated.
And it’s important that you do it as soon as you get the chance to admit it. A delayed apology is worse than a missed apology.
9. Always Be Clearly Documenting Everything – especially architecture and design decisions.
Just like salespeople have their ABCs, we Software developers should follow ABCDEs (Always be Clearly Documenting Everything).
We take a lot of decisions, especially on architecture, design, DB schema, API structure, etc. All these discussions and decisions should be documented somewhere in a central location – whether in the repo or wiki.
And how can we forget about the most important asset that we create? Our Code. Make sure your code is well documented and regularly update the docs whenever things change. Remember, good code readability is a feature in itself for your team.
There will always be a time in the future when we won’t remember why we decided on something, or if someone else works on your project. This document is the best source of the “Why” behind the project’s current state.
Bonus: It helps you cover your ass in case of any discrepancy between teams.
10. Make it Work, Make it Right, Make it Fast
This is one mantra that we skip or do in a different order.
Remember to follow the exact order when solving a problem.
- Get your code to work. It could be badly written, with no abstractions or repeated code.
- Fix your code to use the right design patterns and abstractions, and then refactor it.
- And only in the last part, think about performance and optimization.
In any other order, you are unnecessarily wasting time and resources.
11. Prove the bottleneck before optimizing it
I have to repeat this again because I have repeatedly broken this rule. I have tried to optimize my functions or API calls prematurely. I have spent days thinking about all the different ways I can gain a couple of hundred milliseconds for an API call or look at the query plan and optimize it.
- Are you even sure it is a bottleneck?
- How do you know it?
- Have you measured it?
- If you fixed it how fast your code will become?
Till you can prove it’s a bottleneck and that customers are getting frustrated, you don’t have to optimize it.
12. Track Progress & Measure Results
I touched on this in the previous point a bit. How do you even know something is working as expected or slower than usual? The only way to be sure is to measure it.
We are engineers. We can’t just work off of guesswork or intuition. We need to back it up with actual, real-life data points.
Measuring doesn’t mean just your code or software. It also means measuring yourself. Try to track how you are progressing toward your goals. It will help a lot during that quarterly or annual performance review.
13. Automate whenever needed
Even though we are programmers, we tend to manually do some things at first. Is it even worth spending 2 days writing an automation script and putting it in the cron vs doing it manually every week?
The rule of thumb I follow is: when you are about to do a task manually the third time, sit down and automate it instead.
Another helpful chart is from XKCD which explains how much time you save vs trying to automate some tasks.
Automating repetitive tasks makes them less error-prone and we achieve better predictability and reproducibility. And it doesn’t need a human to remember to do all the steps in the right order on a particular day. It frees up the team’s mental bandwidth.
14. Consistency is Important
Software development, especially in a team would be hard without consistent processes. Using standard formatting style & linting tools, static analysis, repeatable build processes, etc., all enforce consistency in the team.
With consistency, you lower the cognitive load and can focus more on the actual code & logic. Even if new technology is introduced, it’s important to make sure there is a good migration path. The same applies to any APIs that your team develops. If you mix and match REST/SOAP/GraphQL all in one codebase, it makes it hard to onboard new developers.
15. Strong Opinions, Weakly Held
As developers, we all have our own opinions. It’s usually a sign of great passion. But many times it’s pretty hard for us to change those strong opinions.
A sign of a good software engineer is to have Strong Opinions but Weakly Held. When you get some new evidence that disproves your previous opinion, it is important to accept it. Don’t blindly reject it for argument’s sake.
This is an important trait for a developer because it means that you’re constantly learning. If you never change your opinions, you’ll end up doing a lot of wasted work based on old data.
It’s also important to be able to admit when you’re wrong. Even if you don’t like the person who disproves your opinion, don’t fight the data. Be open to change.
16. Know the trade-offs
In software engineering almost everything has a trade-off – starting from the most basic tools and languages you use to what kind of architecture and OS you are building.
Whenever you take a decision, it’s important to truly understand what are the trade-offs. When you choose between A or B, both options might be good but spending some time to understand how it would affect other parts of the product is important.
You should set aside your prejudices so you can come up with solutions in a reasonable direction within the given circumstances. Sometimes, it involves having uncomfortable conversations with all stakeholders and clearly documenting the trade-offs.
17. Focus on quality
One of the best quotes I read about quality was from Steve Jobs and it stuck with me forever.
When you’re a carpenter making a beautiful chest of drawers, you’re not going to use a piece of plywood on the back, even though it faces the wall and nobody will see it. You’ll know it’s there, so you’re going to use a beautiful piece of wood on the back. For you to sleep well at night, the aesthetic, the quality, has to be carried all the way through.Steve Jobs
Whenever I look at any product (be it software or physical), I check how they have built the things nobody will see. That is the real yardstick of how good the product is.
It is really worth it to spend an extra 20% time to build a 100% product than releasing a half-baked product on time.
18. Use the Right Tools, even if it costs money
Many small companies don’t care about getting the best tools for their developers. I have seen software engineers working on slow 5+ years old computers. Using inefficient tools leads to inefficient engineers, who will be frustrated and eventually leave you.
You should also look at investing in the right software – IDEs, Git hosting with code review capabilities, continuous integration, static code analysis, etc. All these help in enhancing the team’s capabilities and ability to quickly deliver good quality software.
19. Set clear expectations
This principle is important not just within the software engineering team, but across teams in the company. Every job requires you to interact with others and it’s best to start it by setting clear expectations. Both what you are supposed to do and what you expect others to do for you to do your job.
Expectations can be as simple as not disturbing during specific hours, to how/when you want to do testing and product releases.
One company I worked in early on had a very ad-hoc release process and developers would fear whenever they had to do a release.
I clearly set the expectations with the product team that releases would happen only during day time when all developers who worked on the release are available. Improved the quality of life significantly.
20. Manage upwards
Any organization of a certain size has managers and a hierarchy. Most people think managing is something that happens top-down. Your manager gives you a list of tasks to work on and you finish it.
Many software engineers prefer to work in silos – even if they are stuck on a problem, they don’t want to accept defeat and ask for help.
You have to realize information needs to flow from the bottom up too. Whenever you work on something and have finished it or got stuck, it’s important that you communicate it with your manager. Speak up and constantly let your manager have the latest information.
It greatly improves team dynamics and makes for trust among the team members and manager.
21. Learn something new every day
I think this is something that doesn’t require too much explanation. Software engineering as a field is quite volatile and it’s important we constantly keep learning new things.
It could be a new language or framework or you could go back to the basics and understand how to solve things using first principles.
Anyone who doesn’t spend time on self-improvement would soon stagnate and become obsolete.
22. Improve upon something every day in your code/product
This is different from the previous point about self-improvement. This is about continuous improvement in your codebase or product. No code is perfect. Especially the code you wrote a few weeks back. You might have taken some shortcuts or accepted some trade-offs.
You should periodically visit the old codebase and see how you can improve it. Remember how you should focus on quality?
Whenever you touch a line of code or function or module, can you leave it slightly better than how you found it? If so, go ahead and do it. Don’t wait for someone to assign that task to you.
23. Unblock others, quickly
Whenever your teammates ask you for some information or a module, how quickly are you to give them that and unblock them?
Everyone has gotten their own work to complete. If you take forever to provide them with the info or code, eventually it’s going to bite you back later.
Remember the golden rule of reciprocity?
Treat others as you would like others to treat you.
Only if you made it a habit to help others by unblocking them, they would help you back when you require something from them.
24. Don’t be afraid to fail
One quote I read online that I love is “If you don’t fail enough, you aren’t trying enough.” With most things in life, failure is the default outcome. As a software engineer or even a team, you shouldn’t be afraid of failure.
Instead, embrace it and try to learn from it. Are you able to find out the cause of the failure and try to minimize it in the future? What steps have you taken to mitigate it completely? Have you planned any fail-safes?
These are all questions you should ask during the post-mortem of any failure and as a team, everyone should try to give their opinions.
25. Have a living, breathing developer’s handbook
An employee/developer handbook is one document that I feel differentiates between companies that just exist vs startups that people love to work in.
One of the earliest handbooks that were released/leaked publicly was Valve‘s and it clearly explained the company culture.
While these examples are not exactly developer handbooks, your team can maintain one in a wiki or GitHub repo. Essential things required for a developer like where they can get the laptop/software access, access to the repository, AWS Console access, CI/CD, how to push and deploy code, etc.
Make sure this is kept updated and well-documented. An old and outdated document is worse than a non-existent one.
This isn’t an exhaustive list of principles, but instead some of the most relevant and popular ones that I try to make my software engineering teams follow.
Even if you follow 2 or 3 principles in your team, you would be in a much better position in a few months.
What are some principles that I missed? Comment below.