Skip to the content.

From junior to senior frontend: the shift is from code to consequences

From junior to senior frontend: the shift is from code to consequences

The jump from junior to senior is not about writing cleverer code. Plenty of juniors write clever code; some of it is the problem. The shift is a change in what you optimise for: from “does this work?” to “what are the consequences of this?” — how it ages, how it fails, how the next person will change it, what it does to the system around it. A junior makes the current task work. A senior makes the current task work and leaves the codebase better able to absorb the next ten tasks. The code is often simpler, not fancier, because simplicity is a consequence-level decision.

Junior optimises the task; senior optimises the task plus its consequences A junior's scope is the current ticket. A senior's scope widens to include how the change ages, fails, and is maintained by the next person. junior"does it work?" senior"and then what?" ages well?fails how?next dev? widen scope
Seniority is a wider blast radius of concern: the same task, but decided with how it ages, fails, and gets maintained held in view.

Junior thinks about now; senior thinks about next

The clearest tell is how each handles a shortcut. A junior reaches into the store from a component because it works and the ticket closes. A senior sees the consequence — a component now welded to global state, un-reusable, un-testable — and pays a small cost now to avoid a large one later:

// junior: works, closes the ticket, quietly couples UI to the store
function Price() { const p = useSelector(s => s.cart.total); return <b>${p}</b>; }

// senior: same feature, but the consequence (reuse, testability) is protected
function Price({ amount }) { return <b>${amount}</b>; }   // a container supplies `amount`

Neither is harder to write. The difference is that the second was chosen with “what happens to this in six months” in mind.

Make decisions the next person can live with

Consequence-thinking shows up most in the boring choices: naming, boundaries, and what you don’t build. A senior resists premature abstraction (a “flexible” system for a case that has appeared once), keeps boundaries crisp so changes stay local, and writes the test that pins the behaviour so the next person can refactor without fear:

// the senior move: a test that lets the NEXT person change the code safely
test("cart total ignores removed items", () => {
  expect(cartTotal([{ price: 10 }, { price: 5, removed: true }])).toBe(10);
});
// now anyone can rewrite cartTotal and know instantly if they broke it

Encode your judgement so it outlasts you

The final step of seniority is scaling your judgement beyond your own keyboard. A mid-level engineer makes good decisions; a senior makes the good decision the default for the team — by turning a repeated review comment into a lint rule, writing the guardrail that keeps the architecture intact, and documenting the “why” so the reasoning survives. That is the same move that matters in an AI-heavy workflow: your value is not the code you type but the constraints and checks you leave behind, because those hold long after and at a scale review cannot. The path is learnable — start asking “and then what?” of every change, protect the boundaries, pin behaviour with tests, and encode the recurring judgement. The presentational-vs-container and harness-atom-guardrail exercises are small reps in exactly this: the first is a consequence-level refactor, the second is turning judgement into an executable default.