Essential insights from Hacker News discussions

The key to getting MVC correct is understanding what models are

Here are the themes discussed in the Hacker News conversation:

The Ambiguity and Misinterpretation of MVC

A significant portion of the discussion revolves around the lack of a clear, universally agreed-upon definition of MVC, leading to its widespread misinterpretation and misuse.

  • "I think nearly every definition of MVC I've read has been different. At this point it just means you split something into three classes as far as I can tell." - pixelworm
  • "This problem that MVC has is similar to the problem with OOP itself, with monads, or with some design patterns: the original/popular definitions were so incredibly abstract and disconnected from real life usage that they ended up being whatever the person implementing it wanted it to be." - whstl
  • "Getting totally lost in several different enterprise software implementations of MVC was a major contributor to my impostor syndrome early in my career. Glad to have some sort of vindication that I wasn't alone" - gundmc
  • "In the web world the controller should ideally only receive http actions and call services or fat models. It should have no business logic, only validation and parsing. In the frontend UI world the controller is bound to UI events and communicates those from the view to the model objects." - dsego
  • "The Model is where your logic is. Everything that is in any way semantically relevant. Views handled display and editing (yes, also editing!). Controllers ... well ... I guess you might have a need for them." - mpweiher
  • "avodonosov: Everyone knows what model is. Almost noone knows what is controller."
  • "mpweiher: Largely agreed, I wrote about the confusion around 10 years ago[1] and wrote 2 years later about the problem of concept capture we have with MVC[2]"
  • "I used to be very confused about MVC and MVCC and what have you---I can't keep design patterns straight in my head (personal limitation)---I finally went down a rabbit hole of trying to figure it out from scratch." - adityaathalye
  • "The reason MVC got so abused was because of RAD frameworks: rapid application development." - mkoubaa
  • "I actually find it useful to have the Model represented by a single object, usually a facade that coordinates all the other Model objects." - mpweiher
  • "In my experience, just MV is largely just fine." - mpweiher
  • "I feel like MVC is trying to get at a core concept of having your application state in one place, your view objects/state in another place, and then having a third piece that updates the application state." - b_e_n_t_o_n
  • "Maybe that's incorrect definitionally, but it makes sense to me." - b_e_n_t_o_n
  • "MVC always seemed fucking pointless to me, like shit code isn't gonna get good if you put it in boxes" - twelvedogs
  • "I've become convinced that the real problem is probably impossible to get away from. Ultimately we want a nice set of reusable UI components that can be used in many different situations. We also want a nice set of business logic components that don't have any kind of coupling with the way they get represented. In order to make this work, we're going to need some code to connect the two, whether it's a 'controller' or a 'view model' or some other piece of code or architecture. However we choose to achieve this task, it's going to feel ugly. It's necessarily the interface between two entirely different worlds, and it's going to have to delve into the specifics of the different sides. It's not going to be the nice clean reusable code that developers like to write. It's going to be ugly plumbing, coupled code that we are trying to sweep into one part of the codebase so that we can keep the rest of it beautiful." - kybernetikos
  • "It would be interesting to read a case study of how the MVC was applied in some larger SmallTalk app." - jpalomaki
  • "MVC came from desktop applications. It was later repurposed to client-server apps when the web arrived, but it was always an impedance mismatch." - anon7725
  • "It's trust and also org chart. One team will make one service, two teams will probably make two." - frollogaston

The Importance of Testability in Design

A strong theme is the value of separating the "model" component to enhance testability, with a critique of frameworks that blur these lines.

  • "A major advantage of pure models is testability. If your conception of a "model" is perversely a user-facing widget, congratulations, you'll need to write UI tests that simulate button presses and other such user actions, and maybe even inspecting pixels to check resulting state. Tests like that are a pain to compose and are fragile since the UI tends to evolve quickly and may also be vulnerable to A-B experiments. Juice ain't worth the squeeze in most cases." - mmahemoff
  • "In contrast, pure model components tend to evolve slowly, which justifies the investment of a comprehensive test suite which verifies things like data constraints, business logic, persistence. If automated testing were seen as a priority, this would be a no-brainer for any serious app. However, testing tends to be underappreciated in app development. This goes some way to explaining why frameworks carelessly fold in M, V, C to the same component." - mmahemoff
  • "Splitting the logic from the state and presentation make the code very testable. You can either have the state as input and you test your presentation, or have the presentation as input (interaction and lifecycle events) and test your state (or its reflection in the presentation). Also this decoupling makes everything more flexible. You can switch your presentation layer or alter the mechanism for state storage and retrieval (cache, sync) without touching your logic." - skydhash
  • "bsaul: after 15 years refactoring poorly designed iOS apps i can guarantee you 100% of the problems come from an insufficiently designed model layer. Apple is to blame, as they give absolutely O clue on that part (only demo apps with structures that don’t scale)"
  • "ednite: I’m still an MVC fan, thanks in no small part to Scott Allen’s teaching (RIP). I agree that if controllers stay tiny and boring, your model stays rich and your app stays portable, testable, and easier to evolve."
  • "izackp: One thing I learned is that every abstraction or indirection makes things slightly harder to read and debug. Observers seem to have been a solution to 'callback hell' before async was a thing. However, it's rife with pitfalls."

The Historical Context and Origins of MVC (Smalltalk)

Several comments delve into the origins of MVC within the Smalltalk environment, contrasting it with later interpretations and implementations.

  • "cypherpunk666: https://en.m.wikipedia.org/wiki/Trygve_Reenskaug THE OG"
  • "RossBencina: I don't think people called it a "model" but back in Windows VisualBasic/Delphi/C++Builder days the path of least resistance was to set up your GUI in a visual editor by laying out all your widgets in the window. Classically Qt can also be used this way. So you have this UI, you can launch the application and the UI displays and basically works, but none of the buttons do anything. But all the widgets have a great API that you can use to set permissible value ranges, set and query state, etc. And the widgets would fire events when things changed. In other words, the widget contains a model, and implements the Observer design pattern. If you wanted to implement MVC with a separate application data model you had to do work to set up a separate model, and keep it in sync with the UI. None of this class of old tools provided any built-in assistance for defining models separate from Widgets, except for some support for binding UI to database queries/results. Of course this was separate from the Smalltalk world, where there were frameworks for building up models out of pre-defined model "atoms" such as an observable number model that you could bind to various views."
  • "whstl: This is not a slight, this is a compliment to Alan Kay. But to compare it to C++ is to miss the forest for the trees."
  • "jibbit: it is less of an oversimplification than 'nothing like what existed either before or after'"
  • "whstl: Clipping the quote like that completely changes the meaning of what I said. But I stand by it: Smalltalk's OOP is indeed very novel, even for today, especially compared to C++/Java, but it's also very different from Simula, especially the early Smalltalk versions."
  • "mpweiher: Hmm...have you looked at Smalltalk-72? Now Alan makes it clear that the inspiration for Smalltalk OO came from Simula (and a bit from the Burroughs 220 and 5000, from Sketchpad etc.), but to say that Smalltalk is just that is a stretch at best."
  • "travisgriggs: For me, one of the examples was control flow. Smalltalk had none. Or, rather, it had one: send a message to an object/receiver. It was cool that ifTrue:ifFalse: was just a message you sent to a Boolean with a closure (or two) as an argument. And various iterations as well, so you could write your own. This you can do in Swift/Kotlin/etc as well of course. Where Snalltalk went next level was that closures were objects to. And you got them to run with messages like value/value:/value:value:, etc. In most languages with anon functions/closure, you just use argument list (i.e. parens) to invoke it. But you can’t send messages to the closure itself. Smalltalk had a host of introspection methods you could send to it, different flavors of evaluation, and of course any of the ones it inherited from Object. But where it got really cool (imo), was that you could add your own methods to BlockClosure. So you could implement all kinds of control messages on anonymous functions yourself. This was cool, it was all objects, all the way down. Where “object” meant things you can add behavior to, and send messages to."
  • "The "big leap" in Smalltalk was the idea that everything is an object and computation is message-passing, not just classes and instances. That’s not from Simula. Simula was more like an extension of Algol with OO bolted on. Smalltalk was a whole new conceptual model, which is not as simple to explain as Simula/C++/Java-style OOP." - whstl
  • "brazukadev: This makes no sense. MVC was created to use with SmallTalk and based on Alan Kay's OOP, of which late binding is one of 3 requirements, not static types."
  • "The only actual interfacing is the HTTP protocol between the server and web browser. But the browser traditionally only acts as a proxy for the user, who is the one being served access to the resources." - LegionMammal978
  • "travisgriggs: As a former and long smalltalker who learned MVC from the ParcPlace crowd… I used to say things like this. M and V were always pretty unambiguous, but “controller” was kind of like “Christianity”, everyone talks like it’s a unifying thing, but then ends up having their very own thoughts about what exactly it is, and they’re wildly divergent."
  • "neilv: Controller seemed fairly straightforward to me initially, when I was first learning Smalltalk (ParcPlace), and I took my simple understanding on faith. My programs were simple, so M was data, V was presentations of the data, C was interaction on the M and maybe V. It only got confusing when I got more experience."
  • "mpweiher: > so M was data, M is the Model. That means the data and all the things you might ever want to do with the data. So any interaction you might want to do from the view is (ideally) a single message-send to the model."
  • "mpweiher: > V was presentations of the data And editing the data."
  • "mpweiher: > C was interaction on the M and maybe V. > It only got confusing when I got more experience. :-)"
  • "avodonosov: One crucial part of the communication patterns that TFA duly notes is that models do not know about views. That means that views only ever pull data from models, models never push data towards views. It also means that in an update, the model just says "I have changed". It does not send the data that changed. The "the model changed" notifications is also the only reason a view gets updated."
  • "mpweiher: Having the view always update itself from the model means that view state is always consistent, even if you miss or skip intermediate updates. Skipping intermediate view updates is important, because the model can potentially change a lot faster than the view can update, never mind the user processing those view updates."
  • "mpweiher: Also, one common misunderstanding (that also leads to Massive View Controllers) is the mistaken belief that views must not edit models, that you must have a controller to edit it. That is actually not in the original MVC paper[3]. In the original paper, views can edit models and that makes things a lot more sensible."

The Confusion and Misapplication of Design Patterns (Including Monads)

The discussion broadens to include other design patterns and abstract concepts, highlighting similar issues of misinterpretation and context-dependency, with monads being a frequent point of comparison and contention.

  • "whstl: This problem that MVC has is similar to the problem with OOP itself, with monads, or with some design patterns: the original/popular definitions were so incredibly abstract and disconnected from real life usage that they ended up being whatever the person implementing it wanted it to be."
  • "whstl: Design patterns suffer because in most explanations the context is completely missing. Patterns are totally useless outside very specific contexts. 'Why the hell do I need a factory when I can use new'? Well, the whole point is that in some frameworks you don't want to use new Whatever, you dummy."
  • "And monads became the comical case, because they are totally okay in Haskell, but once it gets "explained" and migrated to other languages they become this convoluted mostly useless abstraction at best, footgun at worst (thinking of the Ruby one here)." - whstl
  • "frumplestlatz: > with monads There are a set of three short laws that define what a monad is. I’m not sure that really fits in with MVC, OOP, or design patterns."
  • "antonvs: It’s weird that your comment seems to be downvoted. Monads have a precise mathematical definition. Comparing them to those other broad, handwavy things is delusional ignorance."
  • "hurril: You both have good points. But there is monads the mathematical and programmatic concept, and there is also something a little bit handwavy in how these things are incorporated into an application architecture. The latter is what is being used on the one hand in comparison to MVC, etc, on the other. I.e.: a monadic architecture in Haskell is good, but one in Java is going to suck. A sort of half-way point is in The Elm Architecture, which is a sort of deconstructed IO monad."
  • "metanonsense: But isn’t that exactly what GP meant? There is an original, very precise but also very abstract definition (and what is more abstract than category theory). Then people come along who give a different definition that matches the original one in their specific context („three laws in Haskell“). After that people take these three laws and apply them (sometimes overly simplistic) to other contexts („just give it a flatMap in Scala to get a monad“). And at some point the original meaning got lost, and there are competing definitions out."
  • "antonvs: The laws are mathematical ones, that can't be expressed in the Haskell type system."
  • "antonvs: > And at some point the original meaning got lost This is false. The original meaning is a mathematical one, and its use in Haskell conforms to that. That meaning is not "lost", it's the only valid and rigorous definition there is. People who think the meaning is lost are simply ignorant. All they would have to do to correct that ignorance is a minimal amount of research."
  • "RossBencina: Mathematical rigor is only one type of rigor. I think you can make your point without framing attempts to tame real-world complexity as "broad and handwavy," or deriding people's struggle for understanding as "delusional ignorance.""
  • "frumplestlatz: Placing monads in the same category as MVC, OOP, or design patterns is ignorance. Mathematical rigor is also not comparable to whatever sense of rigor might apply to those concepts. 'Delusional' might be harsh; I’d go with 'confused.'"
  • "whstl: Uncalled for. Try to follow the guidelines instead of calling people names."
  • "frumplestlatz: Speaking authoritatively about subjects with which one is unfamiliar is likely to have one’s speech recognized as ignorant and confused. This is not uncalled for, particularly when coupled with a substantive explanation as to why."
  • "antonvs: What about the guidelines for downvotes? If factual comments are downvoted without consequences, then the site becomes nothing but a place for reinforcement of social fads."
  • "whstl: In the parent comment I did qualify my statement, in it I am talking about monads "once it gets 'explained' and migrated to other languages", not about Monad in Haskell and definitely not about Monad laws."
  • "kriops: Monads are well-defined, though. Monoids in the category of endofunctors, anyone? The problem is when implementations aren’t actually monads at all. The same goes for other functional concepts. I wrote a blog about Java’s Optional::map here: https://kristofferopsahl.com/javas-optional-has-a-problem/ It’s the same kind of problem, where naming signals something the implementation is not."
  • "Ekidd: Yes, monads are abstract, but the definition is also very precise. Specifically (using C++/Rust notation for parameterized types), if we have a type "M", we also need: ... . This, in turn, allows defining what you really want: ... . (There are other rules than ones I listed above, but they tend to be easy to meet.)"
  • "Ekidd: Once you have flatMap, you can share one syntax for promises/futures, Rust-style Return and "?", the equivalent for "Option", and a few dozen other patterns."
  • "Ekidd: Unfortunately, to really make this sing, you need to be able to write a type definition which includes all possible "M" types. Which Rust can't do. And it also really helps to be able to pick which version of a function to call based on the expected return type. Which Rust actually can do, but a lot of other low- and mid-level languages can't."
  • "Ekidd: So monads have a very precise definition, and they appear in incomplete forms all over the place in modern languages (especially async/await). But it's hard to write the general version outside of languages like Haskell."
  • "Ekidd: The main reason to know about monads in other languages is that if your design is about 90% of the way to being a monad, you should probably consider including the last 10% as well. JavaScript promises are almost monads, but they have a lot of weird edge cases that would go away if they included the last 10%."
  • "Procaryote: Every new can be a factory or builder, but usually new is the right thing"
  • "Procaryote: I've seen so many singletons that just exist because java lacks conditional compilation, and you need either a test-something or a real-something. Of course I've also seen heaps of singletons that exist for no reason and could just have been a static class, sometimes because of cargo cult, sometimes because "what if we want to make it configurable later?" Such a waste of energy"
  • "Asimpletune: I think OOP suffers in particular because patterns aren’t a mathematically defined thing like monads etc. They’re just a convention and I think that permits misunderstanding and dogmatism. It’s fine though."
  • "RossBencina: Plenty of confusion around monads, at least that was the case last time I checked."
  • "RossBencina: Design patterns are not "just a convention" they are practical solutions to often encountered problems. They are a way of extracting commonly applied, useful solutions and documenting them for reuse. If you go and read a properly documented design pattern I think it's pretty hard to misunderstand what it is, what it's good for, when to apply it, and when maybe don't. But it is definitely possible to misapply them. I'm still living in the shadow of implementing Observer, and then trying to implement undo as an Observer by translating observed events into Commands and placing them on to the undo stack. Messy."
  • "Izackp: We have hidden control-flow and lost intent. They subvert the readability of the developer's intention, in some cases they make you lose the callstack, and it has you searching the project on what code modifies a variable which is a lot harder than searching for a function call. Don't get me started on dealing with handling errors and out of order events. And oh man, is it easy to just avoid using encapsulation and creating a good interface/api for your piece of code."

The "GluePuppy" / Connectivity Problem

A recurring idea is the necessity of code that connects different parts of an architecture (Model, View, Controller, etc.), often referred to metaphorically as "GluePuppy," and the challenge of managing this essential but often messy component.

  • "travisgriggs: One of the early ParcPlace engineers lamented that while MVC was cool, you always needed this thing at the top, where it all “came together” and the rules/distinctions got squishy. He called it the GluePuppy object. Every Ux kit I’ve played with over the years regardless of the currently in vogue lets-use-the-call-tree-to-mirror-the-view-tree, yesteryears MVVM, MVC, MVP, etc, always ends up with GluePuppy entities in them."
  • "mpweiher: I like the term "GluePuppy". It absolutely is a crucial part, or parts. Basically, it defines the architecture of the system, pulls all the objects together and connects them to create a useful system."
  • "mpweiher: It doesn't help that we don't have linguistic support for "glueing things together", we only have procedure calls. So that's one of the issues I am addressing with Objective-S, actual linguistic support for "glue". And yeah, please don't put the rules/distinctions over designing a clean system."
  • "mpweiher: You can't do that with a good OO architecture, because you absolutely need the GluePuppy to tie things together. That one is different from the other modules. If you don't allow for a GluePuppy, but instead insist on uniform modules, you inescapably get procedural decomposition instead of OO decomposition. If you are in an OO language, that means singletons everywhere, because every module needs to know about its dependencies. And that gets you into a world of hurt when you want to do what OO is supposed to be good for, handle variation. Embrace the GluePuppy! It loves you and wants to make your life simpler."
  • "ndriscoll: Ideally a bridge (e.g. a typeclass) with nice language support like Scala has. I'm not seeing what's so ugly. In math, you have an abstract interface for e.g. monoids (your interface like a Table). Then you have e.g. complex numbers as a set (your business model). The you can identify how C is a monoid via addition. And how it's also separately a monoid via multiplication. Same idea. There's nothing "ugly" about having to write down what you mean."
  • "jmkni: Yeah exactly, you can come up with all sorts of abstraction layers but your ugly code has to go somewhere eventually"
  • "kybernetikos: However we choose to achieve this task, it's going to feel ugly. It's necessarily the interface between two entirely different worlds, and it's going to have to delve into the specifics of the different sides. It's not going to be the nice clean reusable code that developers like to write. It's going to be ugly plumbing, coupled code that we are trying to sweep into one part of the codebase so that we can keep the rest of it beautiful."

The Nature of Models: Beyond Simple Data Buckets

There was a discussion about what a "model" truly entails, moving beyond the misconception of it being simply a database table or an anemic data container.

  • "aryehof: This perpetuates the myth that a model is an object. One object. This has lead to todays common misconception that a model is one anaemic data-bucket representing typically a database table."
  • "aryehof: Instead a model is one or more collaborating objects."
  • "hansvm: And what do you call the Model object holding those collaborating objects? Is that not still one object? The article explicitly supports your position that models can be complicated (see the paragraph starting with "to support more complex views")."
  • "mpweiher: I actually find it useful to have the Model represented by a single object, usually a facade that coordinates all the other Model objects. Not sure why this would lead to anemic models, which I completely agree are a common anti-pattern."
  • "mpweiher: In fact, to me it seems rather the opposite would be true: having the single object facade facilitates having a complex model that coordinates all the different pieces to represent a unified view of said model to the views, which can then be very simple and transparent."
  • "mpweiher: In turn, when the models were coupled with views individually, that has tended to lead to exactly that View → DB Table mapping of dumb data objects you rightly criticize."
  • "globular-toast: 'Model' is an overloaded term. In Domain-Driven Design there is the domain model at the centre of the application. One model consisting of many classes (entities and value objects), functions etc. Then there's the ORM thing, particularly active record ORMs, where "a model" means a database table. And things like serialisation libraries (e.g. Pydantic) where "a model" is one type."
  • "globular-toast: Something that changed how I thought of it was in Robert Martin's Clean Code where the says the whole MVC lives in the outer layers of the application. So basically, 'model' is context specific. It depends what part of your application you're talking about. MVC is about building GUIs, that's it. An application usually consists of a lot more."
  • "cheschire: Meh, I disagree. Models can represent data stores, models can represent data views, and models can be for data transfer. This is necessary for zero trust in application design. Traditionalists really seem to struggle with this shift in mentality that, when you are designing a system, trust is where the problems come from."
  • "cheschire: Just as an example, collecting date inpus from a user might be three diferent fields in the view model and only one field in the data model, and be completely diferent data types (int vs datetime). If you are working with a client side application then you may not want to pass the entire object to the client because you don't trust them with all the information, and you cannot trust them to maintain state, so you only transfer the date value in a data transfer object. These are all models with wildly different intents. If you can’t understand the intent of this separation of concerns then you are designing insecure systems."

The Core Problem: The Expression Problem and Language Design

A more niche but prominent theme is the connection between the difficulties in implementing clean MVC (and OOP patterns) and the inherent challenges posed by the "Expression Problem" and limitations in programming language design.

  • "adityaathalye: MVC (and other OOP patterns) work around the fact that the language does not solve the Expression Problem."
  • "MalinGamer123: Understanding the Expression Problem in MVC and OOP"
  • "tonyedgecombe: In case anybody else wondered: https://en.wikipedia.org/wiki/Expression_problem"
  • "hurril: I think that a big problem here is the fact that in OOP, everything is an object, i.e.: a class. And if all you have is a hammer, then .... But it is much better to picture the model, controller and the view as emergent. But implementing this in OOP is too challenging because some things in either of those three domains are going to be a process, or a piece of state or a role, etc."

The Role and Reusability of Controllers

The purpose and potential reusability of the "controller" component were debated, with some arguing that controllers are often tightly coupled to views and thus not reusable.

  • "lukasb: Any implementation of MVC I've seen the V and the C are so tightly coupled the separation seemed artificial. Skill issue?"
  • "andrewflnr: Yeah, it's really hard to tease them apart in a GUI sort of environment, since the input is so tightly tied to the graphical view. Model and View have always seemed pretty obvious to me but I've never gotten a compelling answer as to what a controller is."
  • "jerf: One of the other markers of "true MVC" I look for is that you ought to have pervasive mixing and matching of the pieces. It is common for models to see some reuse in multiple "views" and "controllers", but if all or almost all of your controllers match up to precisely one view, then you're burning design budget on a distinction that is buying you nothing. If you've got strictly one-to-one-to-one matches for each model, view, and controller, then you're just setting your design budget on fire."
  • "catlifeonmars: > if all or almost all of your controllers match up to precisely one view, then you're burning design budget on a distinction that is buying you nothing. This is a really insightful way to frame it."
  • "kqr: Oooh. Now I get it. I've been dismissive of MVC for nearly as long as I've known it but I realise I've only seen the bad versions. What you describe as correct sounds much more sensible."
  • "RossBencina: > if all or almost all of your controllers match up to precisely one view, then you're burning design budget on a distinction that is buying you nothing. Could you give an example? I've never understood how one could possibly reuse a Controller independently of a View. At a minimum any kind of mouse-based direct manipulation requires the Controller having access to the displayed geometry in order to implement hit testing. E.g. how is a Controller supposed to update the selection range in a text editor without screen-coordinate character extent information from the view, or a drawing editor Controller accessing scene graph geometry for object selection, control handle manipulation, etc."
  • "mpweiher: > I've never understood how one could possibly reuse a Controller independently of a View. And you're absolutely right!"
  • "mpweiher: The problem you're seeing is one of the misunderstandings/misinterpretations of MVC, that the controller is for all interactions/editing. It's not. It's perfectly fine for the View to handle this."
  • "jmkni: Yeah exactly, you can come up with all sorts of abstraction layers but your ugly code has to go somewhere eventually"
  • "frollogaston: Last time I cared about MVC was AP Computer Science. Model is fine, but there's no reason for view vs controller. The UIView vs UIViewController thing in ObjC/Swift was one example of the silliness, something for devs to waste time arguing over. React (Native) refreshingly had no such thing. Angular was all about MVC, but they recently slid it down."