DEV Community

Cover image for Rules and Structure Are Not the Same Thing. They're Cousins.
ThatGhost
ThatGhost

Posted on

Rules and Structure Are Not the Same Thing. They're Cousins.

Let me start with two words we tend to use interchangeably, because the whole argument hinges on the difference between them.

A rule is a constraint on how you write code. Name variables this way, order your methods that way, always null-check on the first line, never let a LINQ query run past three statements. Rules are about the surface — the shape of the text on the screen. They're enforced by convention, by reviewers, by linters.

Structure is how the code is actually organized. Which responsibilities live in which classes, where the boundaries are, how data flows from one place to another. Structure is the architecture underneath the text, and it's what determines whether the code is genuinely easy or hard to work with.

These are cousins. They're related, they influence each other, and good versions of both make a codebase pleasant. But they are not the same thing, and confusing them is where a lot of avoidable pain comes from — because we reach for rules to solve problems that only structure can fix.

And rules are not free. Every rule a team adopts is a constraint that everyone has to remember, apply, and enforce forever. A few of those are worth it. But past a certain point they stop making the code better and start making the work worse: development slows down as people second-guess perfectly good code against an ever-growing rulebook, PRs turn into style arguments, and frustration builds between colleagues over constraints that were never really solving anything. Rules carry a cost, and that cost is stagnation and friction on the team.

We keep paying it because we want the code to be perfect. Not working — perfect. Theoretically, structurally, aesthetically clean. It's a good instinct that curdles into a bad habit, because we forget that every rule we add is a burden carried by the people working in the codebase every day.

An example everyone will recognize

Say you have a class that reads some data, transforms it, and writes it somewhere else. All in one place.

The rule-based approach to keeping this clean tends to escalate. First, you add sections:

public void Process()
{
    // --- Read ---
    var raw = _source.Fetch();
    var rows = Parse(raw);

    // --- Transform ---
    var mapped = rows.Select(Map);
    var filtered = mapped.Where(IsValid);

    // --- Write ---
    _destination.Save(filtered);
}
Enter fullscreen mode Exit fullscreen mode

That works until the class grows. So someone introduces a convention: document every read and write operation in XML comments so we can track them. Then that isn't enough either, so a new rule appears — name your variables so their purpose is obvious, readBuffer here, writeBuffer there, so a reviewer can tell at a glance which side of the pipeline they belong to.

Each step feels reasonable in isolation. Together they're a pile of scaffolding propping up a shape that was never good to begin with. You're adding rules to compensate for the absence of structure.

The structural approach asks a different question: why is all of this in one class at all?

public class ImportPipeline
{
    private readonly IReader _reader;
    private readonly ITransformer _transformer;
    private readonly IWriter _writer;

    public void Process()
    {
        var data = _reader.Read();
        var result = _transformer.Transform(data);
        _writer.Write(result);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now the read operations live in their own classes. So do the writes. They're small, repeatable, testable, and — this is the important part — you don't need a rule to tell you which variable is for reading and which is for writing, because reading and writing don't share a scope anymore. The problem the naming rule was solving no longer exists.

You can still add rules here. "Every public reader needs XML docs" is a fine one. But notice what it's doing: it's supporting a structure that already works, not substituting for one that doesn't.

Why we reach for rules first

I think the core mistake is a belief that structure follows from rules. That if you enforce enough stylistic constraints, good architecture will emerge as a byproduct. In reality it's the other way around — good structure makes most rules unnecessary, and no quantity of rules will produce good structure.

Rules win by default because they're cheap arguments. "Just always list everything." "Just document it." "Just rename it." Each is a single sentence in a review, and each sounds responsible. Meanwhile, actually thinking about the underlying structure costs time, focus, and often a bigger change than anyone wants to make on a Tuesday afternoon. So we reach for the bandaid in the PR instead of the fix, and we do it again and again.

The bill comes due in a place we rarely measure: memory. There's a ceiling on how many conventions a person can actively hold while writing code — realistically somewhere around twenty or thirty before they start slipping — and a good chunk of that is already spent on language conventions nobody can opt out of. Every bespoke rule a team invents eats into what's left. Past a certain point people simply cannot keep all of it in their heads, and then we start getting frustrated with each other for failing at a task that was never humanly reasonable in the first place.

The road, and the potholes

Here's how I picture it. Imagine a road full of potholes.

The potholes are the problems — the real structural issues in the code. Rules are our attempt to guide the cars around the holes: swerve here, slow down there, mind the gap. CI, tests, and linters are the cones and tape we put around the holes, plus the guard rails along the edge of the road so nobody drives off entirely.

None of that is bad. Some of it is genuinely necessary — you do need to tell drivers to stay on the road and use the lanes. Rules are not evil, and I'm not arguing for a lawless codebase where everyone does whatever they want.

But notice that guiding cars around a pothole doesn't fix the pothole. Structure is repaving the road. And once the road is smooth, most of the swerve-here-slow-down-there rules just... aren't needed anymore. The need for rules drops considerably when the thing underneath is actually sound.

The rules I'd keep, and the ones I wouldn't

To be concrete about where the line is for me: I fully endorse rules tied to language conventions — underscores on private readonly fields, that kind of thing — and team-oriented ones like every public function needs XML docs. These are cheap to remember, consistent, and they make collaboration smoother without pretending to fix architecture.

The ones I'd push back on are the rules dressed up as structure. "Always use pattern matching for enums." "The first line of every function should be null checks." "Any LINQ query with more than three statements needs an explanatory comment." Each of these is trying to fix a structural smell with a stylistic mandate, and each one quietly assumes the smell is inevitable rather than solvable.

What to actually do

I'm not asking anyone to delete their linter. I'm asking for one change in how teams treat new rules: evaluate every proposed code rule as critically as you'd evaluate a structural change.

A rule should help the structure you have, not stand in for the structure you're avoiding. Before adding a new stylistic rule, make the case for it the way you'd make the case for a refactor — argue that the problem is real, that it's actually a problem, and that a rule is the right tool for it rather than a bandaid over something deeper.

Rules and structure are cousins. They can work beautifully together. But they are not the same thing, and the moment you start using one to do the other's job, you've traded a road you could have fixed for a lifetime of careful swerving.

Top comments (0)