Sebastiano Druda

← Back to writing

Essay / Tools, Infrastructure & Workflows

Tooling Is Good When It Disappears

Choose development tools that make correct work easier. Balance formatting, linting, types and tests against setup friction, exceptions and maintenance.

A useful formatter removes an argument from code review. A useful test catches a behavior change before a user does. A useful development command starts the project without requiring someone to remember a sequence of undocumented steps. These tools improve work by making the correct path easier to follow.

The opposite is also possible. A tool can create configuration disputes, fragile setup and exceptions that only its original installer understands. A sophisticated development environment is not automatically a productive one. Its value depends on the repeated decisions and failures it removes.

A tool earns its place when the correct path requires less thought.

Start with repeated friction

Before adding a tool, identify the work it should reduce. Is the team repeatedly debating formatting? Forgetting a build step? Missing a particular class of defect? Struggling to reproduce the environment? A specific problem makes the tool’s benefit observable.

Avoid selecting tools because a complete stack is expected to contain them. A small static publication does not need every service used by a large application team. It does need a reliable way to validate content, build output and exercise the interactions it actually ships.

The first improvement may be a convention or a short script rather than a new dependency. If three commands must always run in sequence, a named package script can encode that sequence. If one directory contains generated files, a clear ignore rule and documentation may prevent recurring confusion.

Tooling should reduce the burden on ordinary work. A setup that performs impressively during its introduction but requires frequent expert intervention has moved the burden rather than removed it.

Let formatting settle mechanical choices

Prettier’s rationale explains formatting decisions in terms of consistent output. The practical benefit is that contributors can spend less review effort on mechanical style and more on behavior, naming and structure.

A formatter works best when its scope is predictable. Developers should know which files it handles, how to run it and whether CI checks the same result. Editor integration is convenient, but the repository command remains important for people using different environments.

Avoid turning formatter preferences into a second design system. Every exception and custom rule creates maintenance work. Add configuration when it serves a concrete project need, not merely to reproduce every contributor’s preferred whitespace.

Keep broad formatting changes separate from unrelated behavior changes. A consistent repository is valuable, but a noisy diff can make a small feature harder to review. Tooling should support comprehension at the point where another person must assess the change.

Give each check a distinct responsibility

A linter, type checker and test suite overlap in places but answer different questions. A linter can flag suspicious patterns and enforce conventions. A type checker can detect incompatible uses within its model. Tests exercise selected behavior with concrete inputs and environments.

None establishes complete correctness. A well-typed date can be the wrong publication date. A lint-clean component can overflow on a narrow screen. A passing unit test can coexist with a worker asset served under the wrong MIME type in production.

Design the checks as complementary boundaries. For this site, content schema validation checks metadata, editorial unit tests check visibility and ordering, browser tests check interactions, and release assertions inspect generated files. Each contributes evidence relevant to a different promise.

When a new check is proposed, ask what failure it would detect that the current system misses. If the answer is only that it increases the number of tools, reconsider the addition. If it protects a meaningful boundary, make its failure message actionable.

Make local and CI paths recognizable

A contributor should be able to run the relevant validation locally without reverse-engineering the workflow file. Package scripts with clear names help make the same checks available in development and automation.

CI may run in a different environment and include additional release steps, but it should not embody a secret definition of correctness. When a local command passes and CI fails, the difference should be explainable through dependencies, platform, configuration or an explicitly broader check.

Keep the environment reproducible enough for the project’s needs. Pin dependencies through the lockfile, document the supported runtime and avoid relying on globally installed tools whose versions are unknown. This reduces a class of failures that otherwise consume attention without improving the product.

A convention becomes useful when people can rely on it without asking who remembers the exception.

The development-workflow essay develops this idea. Repeated decisions are a cost even when each one is small. A stable command and a clear failure report can remove that cost from everyday work.

Treat false positives as maintenance work

A check that frequently flags acceptable work trains people to ignore it. Disabling every noisy rule is not the answer, but neither is treating the noise as free. Investigate whether the rule matches the project’s actual contract and whether its configuration is too broad.

When an exception is legitimate, make it narrow and explain why it exists. A file containing an exact verification token may need to be excluded from formatting because adding a newline changes the required bytes. That is a specific boundary, not a reason to stop formatting all HTML.

Tests can also become noisy through unstable timing or overly broad assertions. Repair the test so it measures the intended behavior. Increasing timeouts indefinitely may hide an actual performance or synchronization problem without making the result more trustworthy.

The goal is a signal that deserves attention. A failed check should lead a contributor toward a concrete correction or investigation, rather than encourage repeated reruns until the status becomes green.

Keep the toolchain smaller than the problem

Every dependency adds some combination of updates, configuration, security review and compatibility constraints. These costs can be worthwhile, but they belong in the decision. A plugin that saves a few keystrokes may not justify a fragile build integration.

Prefer existing capabilities when they meet the need. A shared helper can prevent duplicated publication logic. A small release assertion can check a known invariant. A standard browser test can inspect overflow without introducing a separate visual platform for a one-off task.

This is not a rule to write everything yourself. A maintained parser is preferable to a homegrown approximation when correctness depends on a real format such as XML. The useful comparison includes implementation risk and long-term maintenance, not just dependency count.

Choose the smallest understandable mechanism that reliably protects the required behavior. Then document the reason it exists so a future cleanup can distinguish an essential boundary from an abandoned experiment.

Feedback speed affects whether checks are used during development. Keep a focused command available for the behavior being edited, while preserving the complete release suite in CI. The focused path should accelerate diagnosis without becoming permission to skip required validation before publication. Clear naming helps: contributors should know whether a command checks one component, the production build or the entire editorial preview contract.

Evaluate tools through ordinary work

A tool earns continued use when its benefits survive beyond setup. Observe whether reviews focus more on behavior, whether new contributors can run the project and whether recurring defects are caught earlier. These are concrete improvements even when they are not reduced to a single productivity score.

Also watch for costs: slow feedback, duplicated commands, confusing output and configuration that changes more often than the product. A tool that once solved a problem may become unnecessary after the architecture changes.

Removing a tool should follow the same evidence discipline as adding one. Identify which checks or workflows depend on it, preserve necessary coverage and verify the resulting path. Deleting an apparently unused package without understanding a build-time import can break work far from the file being edited.

The best tooling often becomes unremarkable. People format code, run checks and release changes without repeatedly deciding how those activities should work. That quiet reliability leaves attention available for the parts of engineering that still require judgment: understanding the problem, choosing boundaries and evaluating whether the product behaves as intended.

Sources

  1. Prettier rationale — checked 2026-10-14

Topics