5 ways to improve engineering velocity

Source: Duolingo Blog•

5 ways to improve engineering velocity

There’s no silver bullet for moving fast, but there are best practices worth stealing!

I’m a TPM. My passion is helping engineers and teams do what they’re best at: build amazing products. One of my current programs is focused on engineering velocity, which means I spend a lot of time asking teams what’s slowing them down and what’s helping them move faster.

Over the past few months, I held a listening tour to get the scoop on what works and what doesn’t. I focused on five projects and asked the same core questions: What worked, what didn’t, and what would you recommend?

None of what follows is one-size-fits-all. Every team and project is different. These are patterns that came up repeatedly, and the teams that applied the ones that fit their situation moved faster. Take what works. Leave the rest.

TL;DR? Check out Quick Reference.

When cross-functional partners find out about constraints too late, it creates additional stress and schedule pressure. Sharing information early puts teammates and partners in a better position to plan, schedule, and avoid emergencies. Important dates can include merge cuts, asset hand-off windows, review cycles, integration deadlines (when frontend needs to wire up to backend or when an asset needs to be done and integrated into the code), and marketing campaigns.

Here is an example of a Slack post sharing plans:

Most features have a dependency chain: Design has to be done enough before engineering can get started, engineering has to be far enough along before QA can start testing, QA has to be done before the merge. That chain is your critical path, the longest sequence of dependent steps that determines your earliest possible ship date. If any step on the critical path slips, the whole timeline slips.

Depending on the criticality of the release, you may also want to plan some time in case something goes wrong. We might get feedback to change the design. The code might hit a gnarly section that takes longer than expected. Or QA might find a bug that has to be fixed.

You don’t need every answer up front, just enough to get started. Map out who needs what from whom and how much time each function needs. Keep it lightweight. This shouldn’t feel heavy. When things change, share the information and adjust the plan. The point isn’t to have a perfect plan. The point is to make sure folks are in the know so they can adapt together.

Build against a contract

Once you’ve mapped out the critical path, the next question is: What can run in parallel instead of in sequence? One good way to do this is to define contracts up front, build against them, and then put the pieces together at the end.

The contract can be as simple as: the endpoint returns { user_id, subscription_type, position}, this way backend builds the real endpoint, frontend builds the UI against that shape.

This can be done for assets, animations, designs, API responses. Build against the contract: the interface, the hooks, and the data shape. Use placeholders. Then wire them together when they’re ready (remember to leave time to do that plus test).

For example, in one project, engineering built against placeholder assets while animations were produced separately and then swapped in the real files. It lets both tracks run in parallel. The one hiccup was when a new animator delivered a file that didn’t follow the same conventions. It would have helped if the contracts were documented clearly and shared.

If a contract changes, be sure to tell everyone so they can make the same adjustments. Here, again, the point isn’t to have a perfect plan. The point is to make sure folks are in the know so they can adapt together.

Shorten feedback loops

Build small, get feedback early, and adapt. This is the Green Machine in action. Set up feedback loops, double down on what works, and stop what doesn’t. Create a rhythm of learning, and feed that back into where to go next.

Prototype, prototype, prototype. Build just enough to learn. Sometimes that’s a Figma mockup to get early feedback. Sometimes it’s a working build with mocks behind it. Sometimes it’s production-quality code from day one. It depends on where you are and what questions you’re trying to answer.

Start small and build it out as you need it. Write code that’s extensible enough to grow into production, especially the parts you expect to iterate on. But don’t invest so much that it becomes hard to throw away if the idea doesn’t work. The teams that moved fastest kept V1 small, built with extensibility in mind, and weren’t afraid to stop what wasn’t working.

One thing to be intentional about: make the gap between a working prototype and production-ready code visible. Articulate what it will take to productionize the prototype so engineering and product share the same expectations about what’s needed before shipping.

Set up direct feedback channels with leadership. Set up a dedicated Slack channel with senior leadership at kickoff. Give key reviewers access to development builds, paired with a short weekly note explaining what feedback would be most useful. One framework we use at Duolingo is the 10/50/99 approach:

  • At 10%, ask for directional input
  • At 50%, align on structure and approach
  • At 99%, focus on polish and details

I’ve found it useful to name the fidelity level when asking for feedback; for example: “This is a rough early build, so we’re looking for 10% feedback on the core mechanic, not polish.” The teams that had this moved faster because they got directional input early.

Post frequent small builds, and bring QA in early. Share builds with PM and design partners constantly. Real-device interaction during development surfaces bugs and UX issues that screenshots can’t. Bring QA in at the beginning. Test small pieces as they’re ready instead of saving all the testing for the end when you’re in crunch time. When you test early, you still have runway to make decisions. When you test late, every bug is urgent.

Stay synced without slowing down. All of this only works if the team stays continuously informed. Stand-ups or regular touch points can be a good way to do that, but keep them short and make sure everyone has a voice. Random fun(?) fact: the word stand-up was intended to convey that it should be fast enough to do on your feet.

Everyone should leave knowing:

  • What’s in progress
  • What they need to work on next and by when
  • If they are blocking anyone
  • If they’re blocked, what they need to do to get unblocked

When something needs more than a minute, put it in a “parking lot” and follow up later with just the people who need to be there. Be sure everyone has what they need, if they don’t, be sure to include that in the “parking lot” and have a conversation after stand-up.

Whenever possible, keep teams small, stable, and focused

The teams that moved fastest were small, stable, and fully focused on one project. Nobody was getting pulled into other work.

Staff the project with everyone it takes to ship. Cross-functional teams move fastest when partners are on the same project rather than splitting time across workstreams. When a designer is focused on your project, they’re not context switching back from something else when feedback comes in. Iteration happens in hours instead of days.

Protect the team’s focus. Ad-hoc requests will always come in. Prioritize ruthlessly and minimize context switching. If the team expects a lot of incoming requests, plan for it. Set aside dedicated time for ad-hoc work so it doesn’t fragment the rest of the schedule. The teams that stayed focused moved faster than the ones that tried to do everything at once.

When team changes do occur, there are things you can do to make it a little easier.

Write it down. Before PTO, individual transitions, or team transitions, capture current state, in-flight tasks, pending decisions, and contacts in a short doc. What’s obvious to the team is invisible to a newcomer. Consider creating a shared project in Claude with working agreements, project documents, or plans. Be sure to share it with the team so newcomers can get up to speed quickly.

Plan for ramp-ups when projects change hands. When a project moves from one team to another, the receiving team needs time to build context. Budget ramp-up time into the plan, and where possible, overlap the outgoing and incoming teams for a transition period. Use that shared project in Claude mentioned above to onboard new team members.

When projects cross boundaries

Not every project fits neatly inside one team. When a project crosses team or pillar boundaries, do an upfront kickoff so everyone understands what’s being built, what domain knowledge is needed, and who to reach out to. With a lot of moving pieces, designate someone to hold the end-to-end view, call out tradeoffs, and catch risks that fall between teams. Engineering, product, and design are each optimizing their domain. But someone needs to be optimizing for the whole. Consider asking your lead about requesting TPM help on the project.

A few more things that helped

Use wait time productively. When waiting on reviews, leadership feedback, or approvals, keep moving. Spec backend changes, refine APIs, or draft documentation. Even if requirements shift, the thinking compounds.

Find the expert. Working in unfamiliar code or across team boundaries? Identify a subject matter expert (SME) and ask for consultation time. Figuring it out on your own almost always costs more than asking.

Plan for tech debt up front. It’s hard to justify refactoring before a project and hard after you ship. Assume some hardening will be necessary and allocate time for it in the plan.

Apply the law of mobility. This is also known as the law of two feet, from the Open Space concept. For meetings where you don’t need everyone all the time, invite team members to use the law of mobility. If an individual is not learning or contributing, let them go someplace where they can use their time wisely.

I hope some of this is useful. If you want to jam on techniques, want more ideas, or have practices that have worked well for your team, I’d love to talk.

Quick Reference

Things to try:

  • Agree on interface contracts up front so work can run in parallel
  • Keep V1 small to validate the core idea before investing more
  • Make the prototype-to-production gap visible; articulate what it takes to ship
  • Set up a direct feedback channel with leadership on day one
  • Name the fidelity level when asking for feedback (10%/50%/99%)
  • Bring in QA early to test small pieces as they’re ready
  • Keep stand-ups short; problem solve later with just the people who need to be there
  • Staff the project with everyone it takes to ship
  • Prioritize ruthlessly and minimize context switching
  • Designate someone to hold the end-to-end view on cross-team projects
  • Write a hand-off doc before PTO or project transitions
  • Create onboarding docs for team conventions
  • Budget for ramp up time if the project is changing to a new team
  • Find a subject matter expert when crossing team boundaries
  • Keep moving during wait time
  • Allocate time for hardening and tech debt
  • Apply the law of mobility (aka the law of two feet) in meetings that allow for it. If you’re not learning or contributing, go someplace you can use your time wisely.

At Duolingo, engineers have the autonomy to move fast, collaborate across teams, and turn feedback into impact. Ready to build with us? We’re hiring!

What this article says