C.W.K.
Stream
Lesson 03 of 04 · published

Adding a View Never Changes the Model

~11 min · invariant, views, open-closed

Level 0Cold Ash
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"If a view needs to change the model to render itself, the view isn't the problem. The model is."

The Hard Invariant

Here is a rule a refactor must never quietly break: adding a view does not touch the model. A view is a pure projection — it reads the model and draws. It derives whatever geometry it needs (frets, keys, beat positions) from the model's existing fields and writes nothing back. A drum grid reads tempo and sections to lay out a beat grid; it does not append anything to the song.

The Mutation Smell

Sometimes you'll start building a view and discover you 'need' to add a field to the model, or tweak its notes, just to make the view render. Stop — that's a smell, and it's diagnostic. It means one of two things: the model is genuinely missing a field that belongs to the musical truth (add it to the model deliberately, for all views), or you're about to smuggle view-specific state into the shared truth (don't — keep it in the view). What you must never do is let the renderer write back into the model to paper over the gap. That's exactly how copies and drift sneak in.

Closed to Views, Open to New Views

This is the open-closed idea wearing a music hat: the model is closed for modification by views, but the system is open to new views. You can add a bass tab, a piano roll, a drum grid forever, and each addition is purely additive — a new subclass, no edits to the truth. The day adding a view forces a model change is the day you learn your model was incomplete. That's useful information, but it's a model decision, made once, for everyone — never a side effect of one view.

Code

A view reads; it never writes back·python
# WRONG — a 'view' mutating the model to render itself:
class BadDrumView(InstrumentView):
    def project(self, song):
        song.notes.append(make_kick(song.tempo))  # model mutation = rot
        return draw(song.notes)

# RIGHT — derive what you need, write nothing back:
class DrumView(InstrumentView):
    def project(self, song):
        beats = derive_beat_grid(song.tempo, song.sections)  # read-only
        return draw(beats)

# Can't render without mutating? The model is missing a field.
# Fix the MODEL (once, for all views) — never the view.

External links

Exercise

Take an app whose data you understand and imagine adding one new view of existing data. Could you add it without changing the underlying data at all? If yes, the data model is healthy. If you'd need to add or edit a field just for the view, you've found either a real gap in the model or view-state leaking into the truth — decide which.
Hint
Ask: 'does this new field describe the THING, or just how this one view wants to draw it?' Describes the thing -> model. Just this view -> keep it in the view.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Pippawarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.