The Defaults I Would Learn First as a Software Engineer
Fourteen software engineering best practices written as defaults: prototype vague requirements, choose boring tools, wait for the third duplicate, write the failing test first, measure before optimising, ask after an hour.
Jul 29, 202613 min read

The short answer: software engineering best practices as defaults
Most lists of software engineering best practices read like laws. The ones that actually help you on a Tuesday afternoon are smaller and more useful than that: defaults. A default is a situation you can recognise, paired with the move that is usually right. You still think, but you start from something specific instead of a blank page.
Every roadmap for learning software engineering is the same list of technologies in a different order, and it fails the same way. It tells you what to study and nothing about what to do when you are sitting in front of a problem at four in the afternoon with an unclear requirement and a deadline on Friday. What experienced engineers carry around is a set of pattern matches. A situation shows up, it rhymes with a hundred previous ones, and a default comes to mind.
Below are the fourteen defaults I would learn first, in the order you tend to need them, each written as a situation + a condition → the move. They are rules of thumb, not commandments. The value of knowing a default is that it gives you something concrete to argue against when your situation really is different.
Why rules of thumb beat a syllabus early in your career
When you are new, you do not lack information. You lack judgement about which information applies right now. A syllabus gives you more information. A default gives you a first move, and a first move is what gets you unstuck.
Defaults also make you easier to work with. A team where everyone writes the failing test before the fix, keeps changes small and asks for help after an hour is a team where reviews are fast and surprises are rare. That is a big part of what I mean in what makes a good software engineer beyond writing code.
Before you write any code
1. Vague requirement + a deadline → build a prototype
Do not try to think your way to clarity in a document. Build the smallest fake version, show it to whoever asked, and watch them react. Nothing extracts a real requirement faster than someone looking at the wrong thing and saying so. If this is a regular problem for you, the question on what to do when your manager gives vague requirements has more.
2. A decision + a reason you will forget → write down the why
Six months from now someone will find your odd choice and assume it was a mistake, and there is a fair chance that someone is you. A short comment next to the code, or a paragraph in the pull request, costs two minutes and saves an afternoon of archaeology. Commit messages count too: say why, because the diff already says what.
3. A new problem + excitement about a new tool → choose the boring one
Novelty is a cost you pay in bugs nobody has hit yet, documentation nobody has written and answers nobody has posted. Dan McKinley's essay Choose Boring Technology describes this as having only about three "innovation tokens" to spend, and argues that boring tools are valuable because their failure modes are well understood. Spend your novelty on the part of the system that is genuinely new, and let everything else be dull.
Coding best practices while you are writing it
4. Done by hand + more than twice → write a script
The third repetition is the signal. Not only because the time adds up, but because a manual sequence is a sequence a tired person will eventually get wrong, and the wrong version is often silent. Deployment steps, data fixes and environment setup are the usual candidates.
5. Two callers + the same logic → wait for the third
Two similar pieces of code often turn out to be different pieces of code that currently look alike. This is the rule of three, popularised by Martin Fowler in Refactoring and attributed to Don Roberts: the first time you just do it, the second time you wince and duplicate anyway, the third time you refactor. Deduplicate too early and you build an abstraction with a flag in it, then another flag, until it is harder to read than the duplication ever was.
6. Confusion + a name → rename it
When you keep having to explain what a variable holds, the name is wrong. Renaming is the cheapest refactor there is and has the best return, because every future reader pays the cost of a bad name and none of them will fix it. Clean Code has a whole chapter on naming, and it is the most useful chapter in the book.
7. Data + a schema → decide it before anything else
Application code can be rewritten in a weekend. Data cannot. Your schema is the one decision that hardens with every row inserted, so give it disproportionate attention before there are a million rows shaped by it. Grokking Relational Database Design and Modeling is a good way to build that instinct.
Making it safe to change
8. Changing code + fear → write tests
Fear is diagnostic. It means you cannot predict what your change will break, and no amount of care substitutes for knowing. Tests are less about correctness than about permission: they are what makes you willing to touch the thing at all. If you inherit untested code, Working Effectively with Legacy Code is the book on getting tests around it.
9. A bug + a fix → the failing test comes first
Reproduce it in a test, watch it fail, then fix it. This proves you understood the bug rather than nudging the code until the symptom moved, and it stops the same bug from returning in eight months wearing a different hat. My post on five kinds of API test covers which kind of test to write where.
10. Hard to test + a design question → the design is the answer
Code that needs six mocks to instantiate is telling you it does too much or reaches too far. The difficulty of the test is a measurement of the design, and listening to it teaches you more about structure than most architecture books. Grokking SOLID Design Principles gives you the vocabulary for what the test is complaining about.
Once it runs in production
11. Slow + a guess → measure it
Everyone's intuition about performance is unreliable, including the intuition of people who are usually right. Donald Knuth's famous line is often quoted without its second half: programmers should forget about small efficiencies about 97% of the time, "yet we should not pass up our opportunities in that critical 3%". The way you find that 3% is a profiler, not a hunch. Otherwise you spend a day making the fast part faster while the real problem sits in a query nobody looked at.
12. Production + silence → logs, metrics and traces
A system you cannot observe is a system you debug by redeploying with print statements while users wait. Decide what you would want to know during an incident and emit it now, because the incident is the worst possible time to find out you are blind. Start with request rates, error rates, latency and the health of whatever your service depends on.
13. An outage + a cause → a blameless postmortem
Ask what made the mistake easy rather than who made it. Google's SRE book describes a blameless postmortem as one that identifies contributing causes "without indicting any individual or team", and puts the reason simply: you cannot fix people, but you can fix systems and processes. Teams that go looking for a person instead get quieter engineers and the same outage again. If you are the one who made the mistake, read made a mistake in production: tell your manager or fix it first.
14. Stuck + an hour gone → ask
Set the limit in advance, because in the moment you will always feel five minutes from the answer. Nobody is judged for asking at sixty minutes. Plenty of people are judged for mentioning on Thursday something that has blocked them since Monday. The question on how to ask for help without looking incompetent covers how to do it well: say what you tried and what you think is wrong.
All fourteen software engineering best practices at a glance
| # | When you notice... | Default move | What it protects |
|---|---|---|---|
| 1 | A vague requirement | Prototype | Weeks of building the wrong thing |
| 2 | A decision you will forget | Write down why | Future readers, including you |
| 3 | Excitement about a new tool | Choose boring | Your debugging time |
| 4 | A third manual repetition | Script it | Silent human error |
| 5 | Two similar callers | Wait for a third | Against the wrong abstraction |
| 6 | A name you keep explaining | Rename | Every future reader |
| 7 | New data | Design the schema first | Data you cannot rewrite |
| 8 | Fear of a change | Write tests | Your freedom to change code |
| 9 | A bug | Failing test first | Against the bug coming back |
| 10 | A painful test | Change the design | Long-term structure |
| 11 | Slowness | Measure | A day of wasted optimisation |
| 12 | A silent system | Add observability | Your next incident |
| 13 | An outage | Blameless postmortem | Honesty on the team |
| 14 | An hour stuck | Ask | Days of lost progress |
What else belongs on this list
A few that nearly made it, and are worth arguing about:
- Keep pull requests small. Google's own code review guidelines suggest that about 100 lines is usually a reasonable size for a change and 1,000 is usually too large, because small changes are reviewed faster and more thoroughly. A four hundred line diff gets approved; a forty line diff gets read.
- Delete code. Removing a feature nobody uses is real work with real value, and almost nobody counts it.
- Say "not this quarter". A technical skill dressed up as a social one.
- Estimate in ranges. You will be bad at estimation forever, so give ranges and say what would narrow them.
- Read code. You will read far more than you write. The ability to open an unfamiliar codebase and find the four files that matter is close to the whole job.
How to make these defaults your own
Reading a list does not change habits. Doing each one a few times on purpose does:
- Pick two per month. For example, "failing test first" and "ask after an hour". Practise only those until they are automatic.
- Use them in code review. When you review someone's pull request, check it against the list. You will spot the defaults faster in other people's code than in your own. How to give code review feedback without creating conflict helps with the tone.
- Write down when a default was wrong. The cases where "choose boring" or "wait for the third" failed you are where your own judgement starts.
- Read the source books. The Pragmatic Programmer and the free Google Engineering Practices guide are where many of these ideas are explained properly.
If you are in your first job, pair this with what changes in your first two years as an engineer.
My honest read
Prototype when the requirement is vague. Write down why. Choose boring tools. Automate the third repetition. Let duplication live until the third caller. Rename when you keep explaining. Treat the schema as permanent. Use fear as a signal to test, and painful tests as a signal to redesign. Measure before optimising. Make the system observable before you need it. Blame the system, not the person. Ask after an hour.
None of this is exotic, and that is the point. The gap between an engineer in their first year and one in their eighth is usually not knowledge of some deep topic. It is that the second one recognises the situation faster and reaches for the boring, correct move without having to think about it. The career growth roadmap and my post on twelve moves that take you from mid-level to senior pick up where these defaults leave off.
Learn it on Sefism
- First job roadmap and the first job cheat sheet
- Career growth roadmap
- Grokking SOLID Design Principles for the design side of testability
- The Pragmatic Programmer and Google Engineering Practices
- The fundamentals worth learning as a software engineer
- Habits that make an engineer worth routing work through
FAQ
What are the most important software engineering best practices for beginners?
Write the failing test before fixing a bug, keep changes small, name things clearly, choose well understood tools, measure before optimising and ask for help after a fixed amount of time. These cover most early mistakes.
What is the rule of three in programming?
It is a guideline for removing duplication: tolerate two copies of similar code, and refactor into a shared function when a third appears. It prevents building the wrong abstraction too early.
Is premature optimisation really bad?
Optimising without measuring usually is, because you often speed up the wrong part and make the code harder to read. Knuth's full quote also says not to ignore the critical 3%, which you find by profiling.
What is a blameless postmortem?
A review after an incident that looks for the systemic causes that made a mistake possible, rather than punishing the person who made it. It keeps people honest about what went wrong.
How long should I stay stuck before asking for help?
Set a limit in advance, around an hour for most tasks. When you ask, share what you tried and what you think the problem is, so the other person can help quickly.
Ask your own question
If your situation is different from anything here, the career questions library has honest answers to the questions students and engineers ask me most.
Want a learning roadmap built around you, written answers to your own questions and a place to discuss your FYP? Create your free Sefism account. Members can also book a one to one session when they want to talk it through.
Stay in touch
- LinkedIn: where I share roadmaps, career advice and honest takes on hiring. Follow, or message me.
- X (Twitter): short practical thoughts on engineering and careers.
- Instagram: quick tips and a more personal side.
- Sefism Discord: meet other students and engineers working through the same job market and share what is working.
- Sefism on YouTube: walkthroughs and talks.
Comments (0)
Comments are closed for now.
No comments yet.