How I work
I build the simplest system that solves the real problem, verify that it actually works, and keep the cost of being wrong low.
Observe reality. Solve the real problem. Make the smallest useful change. Verify it. Keep it reversible. Then stop.
Engineering is a stance toward reality, not a job title.
The work is judged by what it does in the real world, not by the effort, the hours, or the elegance of the process that produced it.
I am responsible for the outcome of what I build. The activity of building is not the outcome.
The strongest engineering move is often to do less than I could.
Restraint is active, not passive. I choose what not to build, what not to abstract, what not to automate, what not to say.
Unnecessary capability is not an asset. It is a liability I have to maintain.
I build so that the work can live for years, not for the demo.
A system I cannot reverse is a bet I cannot afford.
Ship for tomorrow. Build for the years after.
I prefer simple, understandable systems over impressive ones.
Complexity is a cost. Every abstraction, dependency, layer, configuration option, hidden state and special case has to pay for itself. I do not add complexity because it might be useful someday, because a framework encourages it, or because it looks architecturally sophisticated.
KISS and YAGNI are not rules to follow blindly. They come from a more general idea: every piece of complexity should have a reason to exist.
I prefer the simplest solution that is still correct.
That means no speculative abstractions, no architecture for imaginary future requirements, no generic helpers created only because two pieces of code currently look similar, and no infrastructure built before there is a real need for it.
DRY is about meaning, not text. Two blocks of code can look similar and still represent different concepts. In that case, duplication can be cheaper than a shared abstraction with flags and hidden behavior.
The goal is not the smallest amount of code. The goal is the smallest amount of unnecessary complexity.
Software exists to solve a real problem.
Architecture is useful when it makes the system easier to change, understand, operate and verify. Architecture is not useful when the system exists mainly to demonstrate the architecture.
I would rather ship a simple correct solution today than spend three months building the perfect architecture for a product that may change or disappear in six months.
That does not mean "just make it work".
Correctness comes first. Then performance. Then elegance.
A fast implementation of the wrong algorithm is simply a faster bug.
A beautiful architecture that nobody needs is still wasted engineering.
I prefer small changes, short feedback loops and incremental evolution.
Small changes are easier to understand, review, test, deploy, observe and roll back.
A large rewrite creates a large area of uncertainty. A sequence of small changes turns uncertainty into information.
I don't believe in heroic engineering as a normal operating mode.
If a change requires months of work before the first useful result, I first ask whether it can be split into smaller steps.
The best architecture is often the one that allows tomorrow's architecture to emerge from today's working system.
Do not predict the future. Make future changes affordable.
Hidden behavior is a tax.
I prefer explicit dependencies, boundaries, contracts, state, validation, errors, permissions and configuration.
At system boundaries, assumptions should become contracts.
HTTP APIs, events and other interfaces should have explicit schemas, types and error semantics. Client and server should not silently interpret the same JSON in different ways.
Invalid input should fail at the boundary instead of becoming corrupted state that travels through the system.
An explicit error is usually better than a silent
undefined.
I would rather read a little more code than spend an hour discovering what a framework or abstraction secretly did.
Good architecture is about boundaries, not the number of folders.
A structure such as HTTP -> service -> repository is useful when each boundary protects a real responsibility.
Adding five more layers does not automatically make the architecture better.
The same applies to SOLID, design patterns and frameworks.
Use them when they clarify the system.
When the pattern exists mainly because the pattern says it should exist, go back to KISS.
One of the most important rules:
"I think it should work" is not evidence.
I verify.
If the intent has holes, I stop and ask. I do not invent the answer for myself or for other people.
Depending on the problem, verification can mean tests, logs, metrics, reproduction, benchmarks, checking actual service limits, observing production behavior, or testing rollback.
This matters especially for money, permissions, authentication, data, concurrency, idempotency, external services and production infrastructure.
Real systems do not care what the documentation, memory or intuition said.
A production failure once came from assuming an API Gateway message-size limit. The actual limit was different. The solution was to verify the real constraint, chunk the payload and reassemble it.
The lesson is simple: check the fact before designing around it.
When something breaks: logs first, panic later.
I want systems that tell us what is happening.
When production is on fire, the first questions should be:
Only then should we start guessing.
This is also why observability is not an optional operational feature. It is part of engineering the system.
Failures are inevitable.
The goal is not to create a system where nobody can ever make a mistake.
The goal is to create a system where mistakes are detected early, contained, observable, reversible and recoverable.
Fail fast on invalid input. Keep rollback paths.
Do not sacrifice security, data integrity or recovery capability just to hit a deadline.
Speed is useful. Uncontrolled risk is not speed.
Some things are not negotiable shortcuts.
Every meaningful change should be considered through four questions: Security. Scalability. Stability. Simplicity.
The simplest solution still needs to be safe and stable enough for its actual use.
Do not remove access controls, data protection, rollback or observability because "we will fix it later".
Later is often more expensive than doing the boring thing now.
There is no real distinction between feature work and quality work.
If tests are necessary to safely change the feature, tests are part of the feature.
If observability is necessary to operate it, observability is part of the feature.
If technical debt makes the next change significantly harder, some refactoring is part of the work.
But this does not mean turning every ticket into a cleanup project.
Leave the codebase a little better than you found it.
Do not turn a small change into a three-month refactor.
I prefer solving the actual user problem before expanding the system.
Ask: what is the smallest thing that solves the real problem?
Then: build, observe, learn, adjust.
Do not build every possible feature before getting feedback.
Scope control is an engineering skill.
When a requirement asks for dashboards, infrastructure and architecture but the actual problem is "tell me when this job has been stuck for an hour", solve the actual problem first.
The user's problem matters more than the shape of the ticket.
There is no eternal best practice.
Every technical decision trades something for something else: simplicity, speed, consistency, scalability, operational cost, engineering time, reliability, flexibility.
I want the trade-off to be explicit.
Instead of "this is the best practice", prefer:
"This is simpler and faster to ship, but it costs us X."
or "This is more robust, but it adds Y operational complexity."
A technical decision is easier to evaluate when its price is visible.
Frameworks come and go. Libraries come and go. Cloud providers come and go. Architectural fashions come and go. AI tools come and go.
Do not build a religion around them.
Choose the tool because it solves the problem under the current constraints.
Do not reshape the problem to fit the tool.
The framework should serve the system. The system should serve the product.
AI changes implementation speed, not engineering responsibility.
Human owns intent. AI can own execution.
The human decides what should be built, why, the constraints, the architecture, the acceptance criteria, what is safe to merge, and what is safe to deploy.
AI can help with scenarios, test generation, boilerplate, implementation, refactoring and mechanical checks.
The workflow is:
AI should not silently invent the product's intent.
Generated code is still my responsibility once I ship it.
I am especially careful with AI-generated code around authentication, authorization, billing, data and concurrency.
A senior engineer should own outcomes, not just tickets.
A ticket coordinates work. It does not replace ownership.
Real ownership means being able to follow something from an unclear requirement to design, implementation, production and outcome.
The repository boundary is not the end of responsibility.
If I own a part of the product, I should have enough authority to make and land the decisions required to keep it healthy.
When I can trust a person, I give them ownership. Doing their work myself and calling it quality control is not ownership. It is a bottleneck.
I want to assemble the system and lead it when the team is mine. I do not expect the same taste or result in a role with no hiring, no ownership, and no right to pick the roster.
In my frame I decide and I drive to a result. Shrinking to a careful expert inside someone else's frame and calling it maturity is not growth. It is surrender dressed as professionalism.
Responsibility without the ability to make decisions is a broken organizational design.
I prefer disagreement before the decision, not after it.
Argue at design. Bring facts, constraints and trade-offs.
Once the team makes a decision, commit to it and ship.
Do not sabotage the decision because it was not your preferred solution.
There is no value in being permanently right if the team cannot move.
I prefer small teams with real ownership.
I choose people who get results, and I invest in the relations between them. The result is never independent of the team that produces it.
Freedom and responsibility come together.
Freedom is not permission to coast.
It means having enough trust and autonomy to make decisions, while being responsible for the result and for the people who depend on your work.
A strong engineer should make the team stronger, not make themselves impossible to replace.
Delegation, mentoring and shared context are part of engineering.
The goal is not to become the hero who knows everything. The goal is to build a system and a team that can work without heroics.
People make mistakes. Systems fail.
The useful question after an incident is not "who can we blame?"
It is: why was this failure possible, and why did the system allow it to get this far?
A blameless postmortem does not mean nobody is responsible.
It means we investigate the system instead of turning the incident into a ritual punishment.
Fix the problem. Improve the system. Learn. Move on.
Work is important, but it is not the whole system.
Constant urgency is not a sign of high performance.
Firefighting should be an exception, not the normal operating mode.
Heroics are sometimes necessary. Building a system that requires heroics every week is an engineering failure.
I build so the work can last for years. A week of hero work that I cannot reverse is not a push. It is an uncontrolled bet.
I value predictable rhythm, deep work, enough time to think, reasonable on-call expectations, calm communication, autonomy, and time outside work.
The goal is not maximum activity. The goal is useful work that can be sustained for years.
A team that needs everyone exhausted to keep the product alive has not optimized the system. It has simply moved the system's failure mode into its people.
Productivity is not the same as activity.
More meetings do not necessarily create more progress. More tickets do not necessarily create more value. More commits do not necessarily create better software. More hours do not necessarily create better decisions.
Sometimes the correct engineering action is: stop.
Look at the system. Read the logs. Think. Ask a question. Delete something. Or do nothing.
Doing nothing is sometimes the cheapest and safest engineering decision.
Do not create unnecessary motion.
Do not create unnecessary complexity.
Do not create unnecessary risk.
Do not create unnecessary work.
Do not create unnecessary explanations.
Do not solve imaginary problems.
Observe reality. Solve the real problem. Make the smallest useful change. Verify it. Keep it reversible. Then stop.
There is no prize for suffering unnecessarily.
And when nothing needs to be done: don't touch it.