← Learning Pathways Learning Pathway

Junior Software Engineer to Intermediate Software Engineer

🕑 12-24 months Software Engineering

Move from completing assigned tasks to owning features end-to-end, making sound technical trade-offs, and beginning to raise the quality bar for those around you.

🎯 Focus Areas

End-to-End Feature Ownership

An intermediate engineer takes a feature from brief to production without hand-holding. This means clarifying requirements before writing code, designing the solution, implementing it, writing tests, getting it reviewed, deploying it, and verifying it in production. Ownership is about the outcome, not just the code.

Technical Trade-off Reasoning

Every design decision involves trade-offs. An intermediate engineer can identify the options, articulate the trade-offs clearly, and make a defensible choice rather than defaulting to whatever they have seen before. Being able to explain why you chose an approach is as important as the choice itself.

Component-Level Design

You should be able to design a moderately complex component independently - defining its API, data model, error handling strategy, and test surface. The goal is to produce a design a senior engineer can review and refine rather than rewrite.

Mentoring Graduates

Teaching is the fastest way to consolidate your own understanding. Start pairing with graduates, reviewing their code carefully, and helping them navigate unfamiliar parts of the codebase. You learn more about your own understanding when you try to explain something clearly.

Quality Advocacy

Intermediate engineers begin to raise the quality bar through code review, test standards, and technical conversations. This is not about being the testing police - it is about making quality a team norm rather than a personal heroic act.

Skills & Behaviours to Develop

Skills to Develop

  • Design a component from scratch including its public API, internal structure, error handling, and test strategy before writing any implementation code.
  • Break a vague feature brief into concrete engineering tasks with clear acceptance criteria and effort estimates.
  • Write meaningful integration and end-to-end tests that catch real regressions rather than just unit tests that verify your own implementation details.
  • Identify and articulate at least two alternative approaches for any non-trivial design decision before committing to one.
  • Provide code review feedback that is specific, actionable, and improves the codebase rather than just approving to clear the queue.
  • Use profiling and performance tooling to identify bottlenecks in an application you own.
  • Contribute to architectural decision records or design documents, capturing context and rationale alongside the decision.
  • Lead a production incident post-mortem for a minor incident you were involved in resolving.

Behaviours to Demonstrate

  • Picks up a feature ticket and drives it to production with minimal check-ins, proactively communicating status and surfacing decisions that need input.
  • Pushes back on requirements that are technically risky or underspecified, proposing alternatives rather than just raising problems.
  • Provides code review feedback that improves the quality of the codebase rather than approving to clear the queue.
  • Proactively documents decisions and trade-offs so colleagues who were not in the room understand the context later.
  • Pairs with graduate engineers on difficult problems, guiding their thinking rather than just giving them the answer.
  • Advocates for test coverage and code quality in planning conversations, not just in code review.
  • Monitors production health for features they ship and acts on anomalies without being asked.
🛠 Hands-On Projects
1 Design and build a feature end-to-end in your team's codebase, writing the design document first, getting it reviewed, then implementing it and measuring its production behaviour.
2 Take a slow endpoint or process in an existing application, profile it, identify the bottleneck, fix it, and document before-and-after with real metrics.
3 Build a small internal tool that automates a manual process your team uses regularly, documenting the design decisions and sharing it with the team.
4 Run a knowledge-sharing session on a technical topic you recently learned, turning your own learning into a reusable resource for the team.
5 Pair with a graduate engineer on a feature for a full sprint, explicitly practising the skill of guiding rather than doing.
AI Literacy for This Transition
AI for design exploration and critical evaluation
1

Use AI to explore alternative designs for a component you are building by asking it to generate three different approaches with trade-offs, and use that as input to your own thinking rather than a replacement for it.

2

Practice prompt engineering for technical tasks - learn how to frame context, constraints, and goals clearly enough that AI tools produce useful design sketches or code stubs.

3

When using AI to generate non-trivial logic, write your tests first, then use AI to suggest the implementation, and observe how often the generated code passes your tests without modification.

4

Learn to identify common AI failure modes in code generation including hallucinated library APIs, incorrect edge case handling, and overconfident performance assertions.

5

Use AI to critique draft design documents by asking it to identify gaps, ambiguities, or unconsidered failure modes.

6

Start building a personal library of effective prompts for engineering tasks you repeat regularly, and share the useful ones with your team.

📚 Recommended Reading

A Philosophy of Software Design

John Ousterhout

The clearest articulation of what makes software designs good or bad, which is exactly what you need when taking ownership of component design.

Designing Data-Intensive Applications

Martin Kleppmann

Builds the mental models for reasoning about data systems, storage trade-offs, and consistency that every intermediate engineer needs as features get more complex.

The Art of Unit Testing

Roy Osherove

Goes beyond the basics to teach you what a good test actually looks like - the difference between a test suite that gives you confidence and one that just creates maintenance burden.

Accelerate

Nicole Forsgren, Jez Humble, and Gene Kim

Understanding the research behind high-performing engineering teams helps you see why practices like small batches and continuous delivery matter, not just that they do.

The Staff Engineer's Path

Tanya Reilly

Reading ahead to understand what senior and staff levels look like gives you a map for your own development and reveals which behaviours to start building now.

🎓 Courses & Resources

Clean Architecture and Design Patterns

Pluralsight

Builds the vocabulary and mental models for discussing software design at a level beyond individual functions and classes.

Database Design and SQL for Developers

Coursera

Many intermediate engineers discover their SQL and data modelling skills are weaker than their application code skills - this closes that gap.

Site Reliability Engineering: Measuring and Managing Reliability

Coursera / Google

Teaches you to think about systems through the lens of reliability and observability, which changes how you design and test features.

Docker and Kubernetes: The Complete Guide

Udemy

Practical containerisation knowledge for an engineer who needs to own features all the way to production deployment.

📋 Role Archetypes

Review the full expectations for both roles to understand exactly what good looks like at each level.

→ Junior Software Engineer Archetype → Intermediate Software Engineer Archetype