Good Engineers Do Not Hide Delays, Blame Tools, or Wait Silently

Practical lessons on engineering ownership, realistic estimation, early escalation, written decisions, blocker management, project tracking, and end-to-end feature delivery.

An unfinished bridge secured by a central keystone leading across project blockers toward delivery An unfinished bridge secured by a central keystone leading across project blockers toward delivery

Writing code is only one part of software engineering.

A feature can be technically impressive and still fail as a delivery.

It can arrive late without warning.

It can work in one service but break during integration.

It can be marked complete while testing is still pending.

It can depend on an API nobody followed up on.

It can be built by three different people, with each person assuming someone else owns the final result.

After leading engineering work across different projects, I have realised that delivery predictability depends as much on communication, estimation, and ownership as it does on technical skill.

Most delivery problems are rarely caused by one massive technical failure.

They usually come from smaller problems that were visible early but were not handled properly.

An assumption was never written down.

A blocker was mentioned once and then forgotten.

A task was called “almost complete” for three days.

A verbal decision was never shared with the rest of the team.

A board was updated only before the meeting.

An EOD commitment quietly became tomorrow morning.

None of these things looks catastrophic on its own.

Together, they make delivery unpredictable.

Estimation Is a Professional Responsibility

Even when a task involves something a developer has never done before, an estimate is still required.

That does not mean inventing a confident number without understanding the work.

It means approaching the uncertainty properly.

Saying “I have never done this, so I cannot estimate it” is not usually enough.

A better response is:

“I have not implemented this type of integration before. I need two hours for investigation, after which I can provide a more reliable implementation estimate.”

That is still an estimate.

It acknowledges uncertainty without avoiding responsibility.

The goal is not to predict the future perfectly.

The goal is to make the current understanding, risks, and likely effort visible.

Deconstruct the Work Before Estimating It

Large tasks feel impossible to estimate because they are often described as one block.

Instead of estimating “build the payment feature,” break it into parts:

  • Database design.
  • API contract.
  • Business logic.
  • Authentication and authorization.
  • Third-party integration.
  • Webhook handling.
  • User interface.
  • Mobile integration.
  • Error handling.
  • Testing.
  • Deployment.
  • Monitoring.
  • Documentation.

Once the task is broken down, the estimate becomes more grounded.

It also becomes easier to identify which parts are familiar and which parts contain real uncertainty.

A developer may know that the database and API work are straightforward but have no experience with the external provider’s webhook behaviour.

That uncertainty should be visible in the estimate rather than hidden inside one confident number.

Separate What You Know from What You Do Not Know

Not every part of an unfamiliar task is equally uncertain.

A good estimate should separate:

  • Work that follows existing project patterns.
  • Work that requires investigation.
  • Work that depends on another person or team.
  • Work that depends on an external system.
  • Work that may require a proof of concept.
  • Work that cannot begin until a decision is made.

This is much better than adding a random percentage buffer to everything.

Sometimes the correct estimate is staged:

  1. Investigation and proof of concept.
  2. Implementation.
  3. Integration and testing.
  4. Production validation.

This gives the team useful checkpoints rather than one large delivery date based on too many assumptions.

Write Down Your Assumptions

Requirements are often incomplete when estimation happens.

That does not mean the developer should refuse to estimate everything until every detail is perfectly defined.

It means the developer should make reasonable assumptions and write them down.

For example:

“This estimate assumes the endpoint supports only one active configuration per workspace and does not include historical versioning.”

Or:

“This estimate includes the backend implementation and unit tests, but not frontend changes or migration of existing records.”

Or:

“The estimate assumes the third-party sandbox behaves the same way as the documented production API.”

These statements are simple, but they prevent a lot of arguments later.

An estimate is not a guarantee.

It is a forecast based on a particular understanding of the work.

If the understanding changes, the estimate may change too.

The important thing is to make the original understanding visible.

Without written assumptions, two people can agree to the same estimate while imagining completely different scopes.

Add Time for Learning, Integration, and Debugging

Developers sometimes treat debugging as an unfortunate event that happened after the “real work.”

It is part of the work.

The same is true for:

  • Learning an unfamiliar library or platform.
  • Unit testing.
  • Integration testing.
  • Database migrations.
  • Deployment validation.
  • Review feedback.
  • Regression testing.
  • Fixing behaviour that worked locally but failed in the actual environment.

The first working implementation is not the same as a completed task.

An estimate that assumes everything will work perfectly is not realistic.

Buffers should not be random padding added to make the number look safer.

They should be connected to actual risks:

  • Learning curve.
  • Unclear requirements.
  • External dependencies.
  • Legacy code.
  • Environment differences.
  • Cross-platform behaviour.
  • Testing complexity.
  • Integration risk.

An estimate without time for testing and debugging is not an estimate but an optimism written in hours.

Delays Are Acceptable. Surprises Are Not.

Tasks slip.

Dependencies fail.

Requirements change.

Bugs appear in places nobody expected.

That is normal.

What is not useful is discovering at the last moment that something which was supposedly almost complete has not actually moved for two days.

When I am managing work, I do not expect developers to predict every problem.

I do expect them to communicate once they see that the original plan is no longer realistic.

You do not need to wait for the daily stand-up.

You do not need to wait until someone asks for a status.

You do not need to hide the issue because you are still trying “one last thing.”

A simple update is enough:

“This is taking longer because the current API response is inconsistent with the documented contract. I am testing two options. The original target is no longer realistic. I will share a revised estimate after validating the second approach.”

That message is far more useful than:

“Still working on it.”

A good update explains what changed, why it matters, and what happens next.

Use a Clear Escalation Format

When a timeline is likely to slip, the update should not be vague.

A useful escalation contains three things.

Original Estimate

State what was originally committed.

For example:

“The original estimate was to complete implementation and testing by Wednesday afternoon.”

Reason for the Slip

Explain what changed.

For example:

“During integration, we found that the third-party API returns a different payload for partially completed transactions. This behaviour is not covered in the documentation and requires additional handling.”

Revised Timeline

Give a new target based on the current understanding.

For example:

“The revised target is Thursday at 2 PM for implementation and Thursday evening for integration validation.”

The purpose is not to write a corporate incident report every time a task moves by two hours.

The purpose is to give enough information for the rest of the team to adjust their plans.

Over-Communication Is Usually Safer Than Silent Assumptions

I would rather receive an extra FYI update than discover later that somebody assumed a delay was not important enough to mention.

Over-communication does not mean posting every minor thought into a team channel.

It means sharing information that can affect:

  • Another developer’s work.
  • QA planning.
  • Deployment.
  • Client communication.
  • Product decisions.
  • Sprint scope.
  • Release dates.
  • Dependencies.

You may think a small delay does not need to be mentioned because you can probably recover the time.

Meanwhile, someone else may be planning testing, a client update, a dependent feature, or another developer’s work around your original timeline.

The purpose of communication is not to report failure but to keep the shared understanding accurate.

Silence creates incorrect assumptions.

Once those assumptions spread across a team, correcting them becomes much more expensive than sending a short update early.

Document Important Verbal Decisions

Verbal discussions are useful because they are fast.

They are also easy to forget, misunderstand, or interpret differently.

When a call produces an important decision, someone should summarise it in writing on the same day.

That summary may include:

  • What was decided.
  • Why it was decided.
  • What alternatives were rejected.
  • Who owns the next action.
  • Whether the scope or estimate changed.
  • Which dependencies remain unresolved.

This does not need to become heavy documentation.

A clear message in the relevant task, pull request, or team channel is often enough.

The important thing is that the decision should not exist only inside the memories of the people who attended the call.

Written decisions protect the team from confusion.

They also help people who were not present understand what changed.

Keep Enough Evidence to Explain the Work

For complex tasks, it is useful to preserve the evidence behind important decisions.

That may include:

  • Error logs.
  • Terminal output.
  • Benchmark results.
  • API responses.
  • Relevant AI prompts.
  • Chat excerpts.
  • Documentation links.
  • Screenshots.
  • Proof-of-concept results.

This should not be treated as collecting evidence against developers.

It should be treated as preserving engineering context.

When somebody asks why a decision was made, “because it worked on my machine” is not a useful answer.

A short record of the actual behaviour, test result, or limitation makes future debugging and review much easier.

AI Tool Limitations Should Be Escalated Like Any Other Constraint

If a team depends heavily on AI-assisted development, AI tool limitations can affect estimates.

A coding assistant may hit a usage limit.

A model may struggle with a large codebase.

The generated output may repeatedly fail validation.

A tool may become unavailable.

A task that was estimated assuming AI assistance may need to be completed manually.

That is not something to hide or use as a last-minute excuse.

The developer should communicate:

  • What limitation was encountered.
  • Whether another model or tool was tried.
  • Whether the task can continue manually.
  • How manual implementation changes the timeline.
  • Whether future estimates should assume the same limitation.

The AI tool may explain part of the delay.

It does not remove the need to provide a revised plan.

“By EOD” Is Usually Not a Real Estimate

I have become increasingly suspicious of the phrase “by EOD.”

It sounds specific, but it often means:

“I hope I can finish this before I stop working.”

Then EOD becomes late evening.

Late evening becomes tomorrow morning.

Tomorrow morning becomes “just one final issue.”

It is better to give a realistic checkpoint:

  • “The API implementation will be ready by 3 PM.”
  • “I will finish the migration today, but testing will continue tomorrow morning.”
  • “I can share the first working version by 5 PM. It will not be ready for deployment yet.”
  • “The backend changes are complete. I still need two hours for integration testing.”

Specific milestones are better than vague promises.

They force the developer to separate implementation, testing, review, and actual completion instead of treating them as one invisible block.

They also make it easier for the rest of the team to plan around the work.

A Task Is Not Complete Just Because the Code Exists

One reason estimates and updates go wrong is that different people use the word “complete” differently.

For one developer, complete may mean the first implementation is written.

For another, it may mean the code compiles.

For QA, it may mean the feature is deployed to a test environment.

For the product team, it may mean all expected behaviour has been validated.

These are not the same thing.

A useful status update should be clear about the actual stage:

  • Investigation complete.
  • Implementation complete.
  • Unit tests pending.
  • Code review pending.
  • QA deployment pending.
  • Integration blocked.
  • Ready for release.
  • Released and verified.

Saying “done” when three important steps are still pending creates unnecessary confusion.

The code existing is only one part of delivery.

End-to-End Ownership Means Owning the Complete Outcome

A feature is not complete because one layer has been implemented.

A backend developer cannot always stop at “the API is done” if the API has not been integrated or validated.

A frontend developer cannot always stop at “the screen is done” if it still fails against the real backend.

A mobile developer cannot call the work complete if the feature behaves correctly on one device but fails on the target platform.

True ownership means caring about the complete delivery chain.

Depending on the project, that may include:

  • User interface.
  • Accessibility.
  • Responsive behaviour.
  • Backend services.
  • Database design.
  • API contracts.
  • Authentication.
  • Authorization.
  • Business logic.
  • Error handling.
  • Mobile integration.
  • Desktop behaviour.
  • Third-party integrations.
  • Testing.
  • Deployment.
  • Monitoring.
  • Documentation.

This does not mean every developer must be an expert in every technology.

It means the feature should not fall into the gaps between roles.

A developer may need help from another specialist. That is normal.

What matters is that somebody remains responsible for ensuring all the pieces come together.

Ownership Is Not the Same as Doing Everything Alone

End-to-end ownership is often misunderstood as expecting one developer to build the frontend, backend, infrastructure, mobile app, tests, and documentation without support.

That is not what it means.

Ownership means:

  • Understanding the complete feature.
  • Knowing which teams or people are involved.
  • Following up on dependencies.
  • Verifying that the integration works.
  • Communicating risks.
  • Making sure the feature reaches the actual definition of done.

A developer can collaborate with five people and still own the outcome.

Another developer can write most of the code and still fail to own the outcome because they stop caring once their individual part is merged.

Ownership is about responsibility, not isolation.

Own Your Blockers

If your task depends on an API, design, review, credential, decision, or another team, following up is part of your job.

Creating a comment once and then waiting silently for three days is not active ownership.

You may not control when the dependency is resolved, but you do control:

  • Whether the blocker is visible.
  • Whether the correct person has been contacted.
  • Whether the impact has been explained.
  • Whether an alternative exists.
  • Whether the rest of the team knows the timeline is at risk.
  • Whether the dependency is followed up again when needed.

There is a major difference between being blocked and waiting passively.

Sometimes a blocker genuinely cannot be resolved by the developer.

That is fine.

But it should never become invisible.

Keep the Project Board Close to Reality

The same principle applies to task boards and project dashboards.

A board that is updated only before a meeting is not a tracking system.

It is a presentation.

The status should reflect reality closely enough that someone can look at it and understand what is happening without personally messaging every developer.

That does not mean developers need to update every task every hour.

But the board should not remain incorrect for several days.

Updating it every one or two days is not excessive but a basic project hygiene.

The task should clearly show things such as:

  • Current status.
  • What has been completed.
  • What remains.
  • Whether the task is blocked.
  • Who or what it is blocked by.
  • Whether the original estimate is still realistic.
  • The next meaningful checkpoint.

A good board reduces the number of status meetings a team needs.

A bad board creates more meetings because nobody trusts what it says.

Meetings Should Produce Something Useful

I have attended enough meetings where everyone discussed useful things and then forgot half of them by the next day.

A meeting should not end with everyone vaguely remembering that something important was discussed.

To prevent that, I prefer reducing meeting notes into three simple categories.

Action Items

These are things that someone must do.

Every action should ideally have:

  • A clear owner.
  • A concrete outcome.
  • A target date or checkpoint.

“Discuss the authentication flow” is not a strong action item.

“Pankaj will document the proposed authentication flow and share it for review by Tuesday” is much better.

The second version makes it clear who owns it, what should be produced, and when the team should expect it.

Deferred Items

These are ideas that may be useful but are not part of the current scope.

Writing them down matters because otherwise they either disappear completely or quietly sneak back into the sprint later.

Deferred does not mean rejected.

It means not now.

This category helps teams protect the current scope without losing potentially useful ideas.

Dependency Items

These are external decisions, teams, APIs, designs, credentials, or approvals that can block progress.

Dependencies should be actively tracked because they are often where timelines silently die.

A dependency item should ideally identify:

  • What is needed.
  • Who owns the dependency.
  • Which tasks are affected.
  • When the next follow-up will happen.
  • What alternative exists if it remains unresolved.

A meeting that ends with these three lists has produced something the team can actually use.

Engineering Discipline Is Mostly About Visibility

Good engineering management is not about forcing people to predict the future perfectly.

It is about keeping the current reality visible.

A developer may estimate incorrectly.

A dependency may arrive late.

An integration may fail.

A feature may require more work than anyone expected.

None of that automatically means someone has performed badly.

The real problem begins when the team continues operating on information that is no longer true.

Good developers and strong teams correct that information quickly.

They break unfamiliar work into smaller parts.

They identify unknowns.

They write down assumptions.

They include time for learning, testing, and debugging.

They communicate delays before deadlines are missed.

They document important verbal decisions.

They preserve enough evidence to explain difficult choices.

They own blockers instead of waiting silently.

They care about the complete feature, not just their individual layer.

They keep boards accurate enough to be trusted.

They leave meetings with actions rather than memories.

Software delivery will always contain uncertainty.

Discipline does not remove that uncertainty.

It prevents uncertainty from turning into confusion.