Skip to content
Oleksandr Poltavets

Engineering approach

The principles I apply when designing, changing and operating software systems.

Section
Get started
Status
Published
Updated
2026-08-28
Read
3 min

These are the working principles behind the technical decisions described throughout this site. They are not a methodology — they are the defaults I fall back on when a decision is ambiguous, which is most of the time.

Understand the system before changing it#

Most production defects are the result of a correct change applied to a misunderstood system. Before modifying behaviour I read the surrounding code, trace the actual runtime path, and confirm my model against a real device or a real trace rather than against documentation.

This applies at every level: a Fragment lifecycle callback, a Gradle task graph, or the way a vendor HAL reports state. The cost of an hour spent reading is consistently lower than the cost of a change that has to be reverted.

See also

  • Performance — Startup time, jank, memory and battery: measurement first, optimization second.
  • Debugging — Systematic diagnosis of crashes, ANRs, native faults and field-only issues.

Reduce friction#

Engineering throughput is usually limited by friction rather than by difficulty: a slow build, a flaky test suite, an undocumented release step, a manual environment setup. Friction compounds, and it is rarely anyone's explicit responsibility.

I treat measurable friction as a defect class of its own:

  • Build and CI runtimes are tracked and budgeted, not left to drift.
  • Repeated manual work is automated once it happens for the third time.
  • Setup steps are encoded in scripts or convention plugins, not in wiki pages.
  • Flaky tests are quarantined and fixed rather than re-run.

Design for evolution#

Requirements change faster than architectures do, so the useful question is not "is this the right design" but "how expensive is it to change this design later".

In practice that means clear module boundaries, dependencies pointing inward toward stable abstractions, and behaviour that is testable without the framework. It also means resisting abstraction that is not yet paid for — an interface with one implementation is a cost, not a design.

Prefer explicit state#

Implicit state is the most common source of defects that reproduce only in the field: mutable singletons, lifecycle-dependent side effects, and values that exist in two places at once.

I model state explicitly — a single owner, a defined set of valid values, and transitions that can be enumerated. On Android this typically means unidirectional data flow, immutable UI state objects, and side effects confined to well-defined scopes.

Deliver end-to-end#

A feature is not delivered when the code is merged. It is delivered when it is released, observable in production, and understood by the people who support it.

I take ownership across that whole path: implementation, tests, pipeline changes, rollout, monitoring, and the follow-up when the metrics say something unexpected. Working end-to-end also produces better designs, because the constraints of release and support feed back into the code.

Improve the surrounding engineering environment#

Individual output matters far less than the throughput of the team. Wherever I work, I spend a consistent share of time on the environment itself: pipelines, tooling, review practice, documentation, and mentoring.

Summary#

The six principles and the observable signal that each one is actually being applied rather than merely stated.

PrinciplePractical signal
Understand firstTraces and source read before the first commit
Reduce frictionBuild, CI and setup time tracked as a metric
Design for evolutionChange cost, not design purity, drives structure
Explicit stateOne owner per piece of state, enumerable transitions
Deliver end-to-endOwnership from design through post-release monitoring
Improve the environmentA measurable share of time spent on team throughput