From junior to senior: what actually changed
Eight years from a frontend job in Lahore to running twenty-three products alone. The three shifts that mattered, and the one I was slowest to make.
I started as a frontend developer in January 2018, building pixel-perfect interfaces from designs. Since March 2026 I have worked independently on my own products โ twenty-three of them live across the web, Google Play and the extension stores, and twenty-five packages published to npm.
In between: WordPress themes, Laravel and Angular for an education platform, then several years leading delivery on full-stack products and finally as a senior engineer on a SaaS team, owning features across web and mobile.
What changed over those eight years was not the amount I knew. Three things changed, and the last one took the longest.
1. From "does it work" to "how does it fail"
Early on, done meant the feature worked when I used it. The shift is to asking what happens when it does not โ when the network is slow, the response is empty, the user is on a phone, the token expired mid-action, two people edit the same thing.
The concrete version, which took me embarrassingly long: a green build is not evidence. I have personally shipped a typecheck that exited zero on genuinely broken code, because the configuration compiled nothing at all. It reported safety it had never checked, for weeks, and every one of us reading it saw a passing gate.
Now I plant a defect and watch a gate fail before I trust it. A gate nobody has seen go red is a gate nobody has verified. That habit alone has caught more real problems than any amount of care while writing.
2. From writing code to removing decisions
A junior asks how to build the thing. A senior asks whether the thing needs building, and what it will cost in two years when someone else has to change it.
Most of the value I add now is in reducing what has to be true for the system to keep working. Fewer moving parts, fewer places a rule can be forgotten, fewer things a new person must know before their first change is safe.
The pattern I trust most: replace a convention with a constraint. Every discipline in my codebases that survived contact with reality is one that got a mechanism.
- "Do not fetch unbounded lists" became a helper whose limit is a required argument, so the failure mode is a compile error rather than an outage.
- "No
console.log" became a lint rule at error level with one exempted file.
- "Every user-visible string goes through the translation layer" became an AST lint rule โ because a hardcoded string passes typecheck, lint, build and every review that reads for correctness.
Each of those started as a note in a document that everybody agreed with and nobody followed. Not because they were unreasonable, but because a rule you have to remember is a rule that decays, and the person who forgets is usually the person who wrote it.
3. From avoiding blame to owning outcomes
This was the slow one.
The junior instinct when something breaks is to establish that it was not your fault. It is a completely understandable instinct and it is corrosive, because it optimises for looking careful rather than for the system working.
What changed it for me was working alone, where there is nobody else for it to have been. Then the only useful question is what would have caught it, and that question produces gates, checks and constraints instead of explanations. Every mechanism I described above exists because something broke and the honest answer to "what would have caught this?" was "nothing".
The most useful sentence I learned to say: "I do not know, let me find out." It costs nothing, and pretending is the expensive alternative.
What did not matter as much as I expected
Frameworks. I have shipped production work in Angular, React, React Native, Laravel, Flutter and vanilla JavaScript, and the transfer between them is close to total once you understand the problems they are each solving.
Titles. I was a team lead before I was, by any real measure, a senior engineer. The title arrived years before the judgement did.
Being the smartest person in the discussion. Almost never the constraint. The constraint is usually that nobody has written down what "done" means.
What working alone teaches that a team cannot
Since March 2026 I have worked entirely on my own products, and it has been the most educational period of the eight years โ not because it is better, but because it removes every buffer.
On a team, a bad architectural decision is absorbed. Somebody notices in review, or the person who inherits it works around it, or it becomes a known quirk everybody routes around. Alone, every decision comes back to you personally, at the exact moment it becomes expensive, and you cannot argue with it because you are the only one who could have made it.
That is a brutally efficient feedback loop. I have changed more of my habits in a year of this than in the four years before it, and almost all of the mechanisms I described above date from it.
What it does not teach is anything about working with people, which is most of the job for most engineers. Code review as a craft, disagreeing productively, explaining a trade-off to somebody who will make the decision, knowing when your objection is important and when you should let it go โ none of that improves when there is nobody there. I got those years from leading delivery on a team, and I would not have got them any other way.
The advice I would actually give
Read code you did not write, deliberately, on purpose, as an activity. Most engineers only read code when they have to change it, which means they only ever see it under time pressure. An hour spent reading a well-built codebase with no obligation to modify it is worth several tutorials.
Keep a list of the bugs you caused. Not to feel bad about โ to notice the pattern. Mine was overwhelmingly about trusting that something worked because it looked like it worked, which is why almost every discipline I now hold is about producing evidence rather than about being careful.
And write for other people, including future you. Half of what I know is in comments explaining why something is the way it is, and those comments have saved me more time than any tool I have adopted.
What I would tell myself in 2018
Ship things and maintain them. Maintenance is where you learn โ a codebase you have carried for two years teaches you things no tutorial can, because it shows you the consequences of your own decisions at a distance where you cannot argue with them.
And write down the reasoning, not just the result. Every "why is this like this?" I cannot answer is a decision I have to make again, and I will probably make it worse the second time, because the first version at least had the context.
https://aoneahsan.com/blog/junior-to-senior-developer-journey