elm-conf
Modeling application state with custom types and opaque structures eliminates illegal permutations at compile time, removing the need for defensive runtime validation while insulating public APIs from breaking refactors.
AI-assisted summary of "Making Impossible States Impossible" by Richard Feldman — original source. Check the original for full context.
Source published:
Key ideas
- Rather than relying on defensive validation pipelines and sorting logic to catch illegal states at runtime, developers should design types that make invalid configurations unrepresentable at compile time. (4:00)
- Managing parallel collections or multiple independent Maybe fields frequently introduces out-of-sync logic errors, which can be eliminated by coupling data into single enriched records or custom union types. (9:02)
- Using a zipper data structure for sequential navigation guarantees non-empty collections and prevents disconnected active states by structurally embedding the focused item between previous and remaining elements. (14:14)
- Opaque types that hide module constructors behind stable accessor functions prevent downstream consumers from coupling to internal record structures, enabling breaking data model refactors without impacting consumers. (17:28)
Detailed notes
## Background Published on YouTube by elm-conf on September 19, 2016, this presentation features Richard Feldman addressing techniques for Elm model design. The talk explores how developers can leverage Elm's strong type system and compiler to rule out invalid application states and eliminate time-consuming bugs from the outset.
## TL;DR Drawing from his experience creating the elm-css library, Richard Feldman explains how designing data structures around domain constraints eliminates the need for defensive validation and reordering logic. He demonstrates this philosophy in application code through a survey interface, illustrating how to couple interdependent prompt and response fields, model guaranteed non-empty lists, and use zipper data structures to represent active selections safely. Feldman further explains how opaque types and accessor functions protect downstream consumers from breaking changes during internal refactors. Finally, he outlines practical design heuristics, such as replacing combinations of multiple Maybe values or parallel lists with domain-specific custom types.
## Atmosphere & style The presentation carries a warm, conversational, and modest tone, deliberately trading flashy spectacles for accessible, practical insights. Feldman connects with the audience through self-deprecating humor—joking about the quirks of CSS layout, Murphy Randle's preceding talk, and random cat pictures—while methodically building toward clear architectural revelations. Watching the talk feels like an encouraging, pragmatic masterclass that delivers approachable 'aha' moments for real-world software development.
## Bulletin - [04:00](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=240s) Rather than relying on defensive validation pipelines and sorting logic to catch illegal states at runtime, developers should design types that make invalid configurations unrepresentable at compile time. - [09:02](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=542s) Managing parallel collections or multiple independent Maybe fields frequently introduces out-of-sync logic errors, which can be eliminated by coupling data into single enriched records or custom union types. - [14:14](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=854s) Using a zipper data structure for sequential navigation guarantees non-empty collections and prevents disconnected active states by structurally embedding the focused item between previous and remaining elements. - [17:28](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1048s) Opaque types that hide module constructors behind stable accessor functions prevent downstream consumers from coupling to internal record structures, enabling breaking data model refactors without impacting consumers.
## Key takeaways - [01:17](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=77s) Modeling strict specification requirements, such as the ordering rules of CSS at-rules, fails when using a naive flat list because the type system allows illegal permutations to compile. - [04:00](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=240s) A defensive 'validate and sort' pipeline creates unnecessary complexity and requires exhaustive test suites to catch edge cases that shouldn't exist in the first place. - [06:22](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=382s) Restructuring stylesheet models into distinct typed fields (e.g., Maybe String for @charset and dedicated lists for imports and namespaces) guarantees deterministic, spec-compliant output. - [06:22](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=382s) While automated test suites provide value, preventing invalid states entirely through the compiler's type system provides stronger guarantees with significantly less maintenance overhead. - [09:02](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=542s) Storing survey prompts and responses in separate lists permits corrupted states where answers exist without associated prompts. - [09:02](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=542s) Coupling related attributes into a single record within a single collection enforces relational integrity directly in the data structure. - [11:21](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=681s) Enforcing non-empty collections at compile time can be accomplished by structuring the model as a mandatory first item alongside a list of remaining items. - [14:14](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=854s) Maintaining a standalone pointer to a current item allows the pointer to reference data missing from the primary list, creating disconnected state bugs. - [14:14](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=854s) A zipper list partitions state into previous items, an active item, and remaining items, simultaneously guaranteeing non-emptiness and valid focus. - [15:51](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=951s) Exposing internal record fields directly to external callers turns internal data model improvements into breaking changes across the codebase. - [17:28](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1048s) Opaque types wrap internal records inside single-constructor custom types without exporting the constructor, restricting pattern matching to the defining module. - [17:28](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1048s) Unwrapping opaque types directly within function argument definitions provides a concise alternative to explicit case expressions. - [18:54](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1134s) Providing public accessor functions decouples the consumed API shape from internal representation, letting library authors optimize data structures invisibly. - [21:25](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1285s) Pairing multiple Maybe fields for interdependent UI elements (such as notification text and undo data) allows nonsensical combinations, like orphan restore targets. - [21:25](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1285s) Replacing multiple Maybe fields with custom union types guarantees that contextual data only exists when the appropriate application state is active. - [23:38](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1418s) Core Elm architecture rules of thumb: use custom union types instead of multiple Maybes, enrich single lists instead of synchronizing parallel lists, and hide constructors to protect internal structures.
## Detailed walkthrough ### [00:10](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=10s) Opening elm-conf and Setting Expectations
Before diving into his presentation, Richard Feldman pauses to ask the audience for a standing ovation for the conference organizers. He remarks on how unbelievable it is that this is their very first conference, given the sheer caliber of the presentations throughout the day. Feldman jokes about watching each speaker and feeling relieved that he did not have to follow them, only for Murphy Randle to take the stage right before him and blow everyone away as well. He expresses how thrilled he is to belong to such a vibrant community where the overall quality has been extraordinary.
Transitioning to his own talk, the final presentation of the day, Feldman sets expectations by noting that this session will be more relaxed and practical rather than featuring a flashy, mind-bending demonstration. As Richard Feldman explains, “Hopefully though this is going to be something practical, something where you can walk away with less of a ‘whoa’ and more of an ‘aha’—so that’s what we’re going for here: making impossible states impossible.”
[01:17](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=77s) elm-css and the Obscure Rules of the CSS Specification
Feldman quips that to establish himself as a reasonable, sensible speaker, he naturally decided to build a CSS preprocessor in Elm called elm-css. In elm-css, developers write Elm functions to generate CSS stylesheets or inline styles. Instead of standard CSS declarations, the code consists of Elm function calls like `body` with styling properties such as padding, minimum width, and overflow. A core design goal of elm-css is to guarantee that it only ever emits valid stylesheets that a browser will accept, catching errors either at compile time or through a validation step.
Digging into the CSS specification revealed countless ways to produce invalid stylesheets, particularly within the section covering at-rules like `@media`, `@font-face`, and three specific directives: `@charset`, `@import`, and `@namespace`. When Feldman asks the audience for a show of hands on who has used all three, hardly anyone raises a hand, confirming it as a dusty corner of the spec. The ordering and syntax rules governing these three directives turn out to be extraordinarily strict:
- `@charset` must be the very first character of the stylesheet—even a single preceding newline invalidates the entire sheet—and declaring more than one is strictly disallowed. - `@import` rules must appear before any `@namespace` declarations and before regular style declarations, but they must follow `@charset`. - `@namespace` rules must appear after `@import` and `@charset`, but before any standard style declarations.
[04:00](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=240s) The "Validate and Sort" Trap and Evan's Advice
Feldman’s initial intuition was to replicate the familiar look and feel of standard CSS files in Elm by converting at-rules into function calls, creating a single homogeneous list containing `charset`, `import_` (with an underscore because `import` is an Elm reserved keyword), `namespace`, and regular style selectors like `body`. However, because they resided in a single list, Elm’s type system required every item to share the exact same type: `Declaration`. This meant a user could easily place a style first, alternate haphazardly between imports and namespaces, or define multiple character sets at the end of the file. The code would compile without issue, but naively serializing it to disk would violate every single rule in the CSS specification.
Feldman initially devised an imperative workaround: a two-step "validate and sort" pipeline. Under this plan, the library would emit a validation error if someone declared multiple `@charset` rules, and it would silently reorder misplaced rules at emit time by sorting them into their legal sequence. Realizing how many permutations could go wrong—intermingled namespaces, duplicated charsets, inverted imports—he prepared to write an extensive test suite. Before committing to this path, he showed the design to his coworker Evan Czaplicki, the creator of Elm. As Richard Feldman recalls, “He gave me a really interesting piece of advice that was sort of a perspective I hadn’t really considered here, which was: what if you just make representing invalid stylesheets impossible?”
```mermaid flowchart TD accTitle: Naive CSS Processing Pipeline accDescr: Diagram showing how a flat list of declarations requires complex validation and sorting to produce valid CSS.
subgraph Input ["Flat Elm Model"] D["List Declaration<br/>(charset, import, namespace, styles mixed)"] end
subgraph Processing ["Defensive Pipeline"] V{"Validate"} S["Sort & Reorder Rules"] E["Emit Error on Duplicates"] end
subgraph Output ["CSS Artifact"] CSS["Valid CSS File"] end
D --> V V -->|"Invalid (e.g. 2 charsets)"| E V -->|"Pass"| S S --> CSS ```
[06:22](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=382s) Designing Unrepresentable Invalid States
Reflecting on Czaplicki’s advice, Feldman recognized that he had blindly copied CSS’s loose, flat list structure simply because CSS did it that way. In his humorous aside, he jokes, “If CSS told me to jump off a bridge, what would I do? Well, probably I’d get the positioning wrong, I’d forget to clear a float, so the bridge would not be able to—anyway.” The realization was that treating all of these constructs as the same type was an anti-feature. Instead of a single list of declarations, the stylesheet data model was redesigned to enforce the spec's structural constraints directly through distinct types:
- **Charset**: Modeled as a `Maybe String`, cleanly enforcing that there can only ever be zero or one charset. - **Imports**: Modeled as a dedicated `List Import`, preventing imports from intermingling with namespaces or styles. - **Namespaces**: Modeled as a dedicated `List Namespace`, isolated to its own contiguous block. - **Declarations**: Modeled as a dedicated `List Declaration` for normal styling rules.
```mermaid flowchart LR accTitle: Impossible States Made Impossible Data Model accDescr: Structural mapping showing how typed record fields enforce CSS spec rules natively.
subgraph Stylesheet ["Typed Stylesheet Record"] CS["charset: Maybe String<br/>(Zero or one allowed)"] IM["imports: List Import<br/>(Contiguous block)"] NS["namespaces: List Namespace<br/>(Contiguous block)"] DC["declarations: List Declaration<br/>(Styles block)"] end
subgraph Serializer ["Deterministic Output Generation"] CS -->|"1. Emit if Just"| CSS["Valid CSS Stylesheet"] IM -->|"2. Emit in order"| CSS NS -->|"3. Emit in order"| CSS DC -->|"4. Emit in order"| CSS end ```
With this model, generating valid CSS becomes straightforward: serialize the `Maybe String` if present, emit the imports, emit the namespaces, and finally emit the declarations. Invalid arrangements can no longer compile, eliminating the need to write tests for impossible permutations. As Richard Feldman summarizes, “One of my takeaways from this is like, you know, tests are good, tests are good, but impossible is better.” Furthermore, a cleaner data model produces an inherently clearer API, nudging consumers toward correct structures. Feldman then poses the broader question: rather than limiting this technique to library design, can we apply the exact same philosophy to regular Elm applications and surveys to make impossible application states impossible?
[09:02](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=542s) Coupling Prompts and Responses
Richard Feldman transitions to another scenario: building a survey interface. The interaction consists of presenting the user with a prompt—such as asking "what was the best part about elm-conf"—accompanied by a small text field to collect their response. A series of these prompt-and-input pairs appear in a row to step the user through several questions.
A naive model might separate these into two independent collections: a `List` of prompts alongside a `List (Maybe String)` of responses, where the strings are wrapped in `Maybe` because an answer might not yet be provided. As a user completes questions, these entries flip from `Nothing` to `Just`. Feldman illustrates an example where prompts ask:
1. *"what is your favorite elm-conf memory"* 2. *"is this the real life"* 3. *"is this just fantasy"*
The responses list contains `Just "all the crowd surfers weren't they amazing"`, followed by `Nothing` and `Nothing`.
```mermaid flowchart TD accTitle: Independent Lists vs Paired Record accDescr: Comparing uncoupled lists of prompts and responses to a single list of coupled prompt-response records.
subgraph Problematic["Problematic State: Independent Lists"] P["prompts: []"] R["responses: [Just 'Yes']"] end
subgraph Solution["Robust State: Coupled Record"] L["List Question"] Q["Record Question: { prompt: String, response: Maybe String }"] L --> Q end ```
The flaw in the two-list design becomes evident when the model enters an impossible state: zero prompts, yet a response list populated with `[Just "Yes"]`. Feldman emphasizes that this indicates a breakdown in business logic: “This should be impossible. If this ever if we ever end up in this model state, that means we had some business logic that broke somehow—not exactly clear on how, but somehow something went wrong.”
Allowing the compiler to enforce correctness avoids tedious debugging sessions spent stepping backward through application state. The fix is straightforward: instead of maintaining two independent lists, define a single record containing both a `prompt` field and a `response` field, and maintain a `List` of those records. By coupling the two fields, a response cannot exist without an accompanying prompt.
[11:21](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=681s) Enforcing Non-Empty Survey State
Feldman expands the survey feature set to include navigation controls. Rather than forcing sequential completion, the application needs forward and backward controls so users can skip a tricky question to return to later, or move back to amend an earlier entry.
To manage this, the navigation data is isolated into a dedicated `History` model. The initial revision contains a `questions` list alongside a `current` question field pointing to the active prompt. For instance, in a pastry survey containing cake, pie, and cookie questions, setting `current` to pie represents being mid-survey. However, this structure permits an impossible state: `questions` can be empty while `current` holds an active question about pie.
```mermaid flowchart LR accTitle: Non-Empty List Construction accDescr: A non-empty list represented by a dedicated first element consed onto a list of remaining elements.
subgraph NonEmptyList["Non-Empty List Representation"] F["first: Question (Cake)"] O["others: List Question [Pie, Cookies]"] end
F --> Total["Conceptually: Cake:: [Pie, Cookies]"] O --> Total ```
A survey with zero questions is non-functional; the model should enforce having at least one question whenever an interactive survey exists. Drawing an analogy to pattern-matching list deconstruction, Feldman refactors the record to store a mandatory `first` question alongside an `others` list of subsequent questions:
- `first: Question` - `others: List Question` - `current: Question`
In this model, cake serves as `first`, while pie and cookies populate `others`. Feldman jokes that “with their powers combined they give us Captain Plan—some some sort of list of questions.” Representing the collection as a guaranteed initial element consed onto a list makes an empty state impossible at the type level.
[14:14](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=854s) Preventing Disconnected States with a Zipper List
Even with a guaranteed non-empty list, a secondary flaw persists: `current` can reference a question completely absent from `first` and `others`. Feldman demonstrates this failure case by setting `first` to cake, `others` to pie and cookies, and `current` to ice cream. Because ice cream appears nowhere in the available question pool, submitting an answer introduces undefined behavior.
To resolve this mismatch, Feldman turns to a zipper data structure: “Can we make it impossible? Yes we can, by using what's called a zip list, so I'm going to channel Tessa Kelly here and introduce a new data structure.”
```mermaid flowchart LR accTitle: Zipper List Data Structure accDescr: A zipper list composed of previous items, a focused current item, and remaining items.
subgraph ZipList["Zipper Structure"] P["previous: [Cake, Pie]"] C["current: Cookies"] R["remaining: [Ice Cream]"] end
P --> Full["Combined Survey History"] C --> Full R --> Full ```
A zipper list partitions the sequence into three explicit fields:
- `previous: List Question` (e.g., cake, pie) - `current: Question` (e.g., cookies) - `remaining: List Question` (e.g., ice cream)
This layout completely prevents disconnected states. The active item is structurally embedded in the collection rather than referenced independently. Furthermore, an empty survey remains unrepresentable: even if `previous` and `remaining` are empty, `current` must still hold a value.
[15:51](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=951s) Refactoring Encapsulation and Breaking Changes
Feldman moves to the upgrade experience—displaying a slide featuring a cat wrapped in a burrito, while admitting, “I just like pictures of cats, there's nothing deep here.” He outlines the core API designed around the `History` record:
- `back: History -> History` (steps `current` back by one) - `forward: History -> History` (advances `current` forward by one) - `answer: Answer -> History -> History` (records a response, converting `Nothing` to `Just`) - `init: Question -> List Question -> History` (constructs a history from a starting question and remaining items)
```mermaid flowchart TD accTitle: Internal Model Refactoring Dilemma accDescr: Transitioning internal fields from a flat questions list to a zipper list breaks external callers that inspect fields directly.
subgraph OldShape["Previous Shape"] O1["questions: List Question"] O2["current: Question"] end
subgraph NewShape["Zipper Shape"] N1["previous: List Question"] N2["current: Question"] N3["remaining: List Question"] end
CallSite["External Call: history.questions"] OldShape -.->|"Refactored into"| NewShape CallSite -->|"Compiles against"| OldShape CallSite -.->|"Fails against (Missing Field)"| NewShape ```
While transitioning from a simple list plus a pointer to a zipper list prevents illegal states, it introduces an API challenge. The internal record fields changed: the original `questions` field was replaced by `previous` and `remaining`. Any external code directly accessing `history.questions` breaks immediately on compilation. Ideally, an internal refactoring to harden the data representation should not disrupt existing callers that rely on the established contract of the module.
[17:28](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1048s) Opaque Types and Encapsulating Internal State
Upgrading internal data structures to rule out invalid states is great, but if downstream consumers depend directly on those structures, every refactor turns into a breaking change. Feldman asks: “Can we make it so that depending on certain implementation details is impossible? Can we make it so that anyone who's using our API doesn't have to worry about getting broken when we upgrade?” The solution in Elm is to use an opaque type by declaring a single-constructor union type rather than a simple `type alias`:
```elm type History = History { previous: List Question, current: Question, remaining: List Question } ```
Wrapping the record inside a single-constructor custom type requires unwrapping it whenever functions like `back` are implemented. While that can be done with a standard `case history of History { previous, current, remaining } ->...`, Feldman highlights a much cleaner syntax: pattern matching directly inside the function's argument list, writing `back (History { previous, current, remaining }) =...`. As he recommends, “if you're going to use this technique I recommend doing it this way tends to be pretty nice.”
```mermaid flowchart TD accTitle: Exposing Opaque Types in Elm accDescr: Module boundary hiding the single constructor History while exposing the type and accessor functions.
subgraph InternalModule["History Module (Internal)"] TypeDecl["type History = History Record"] Logic["Internal Implementation (Zipper / List)"] Accessors["Helper functions: questions, current"] end
subgraph ExposedInterface["Exposed Public API"] PubType["type History (Constructor Hidden)"] PubFuncs["questions: History -> List Question<br/>current: History -> Question"] end
subgraph Consumers["API Consumers"] Annotations["Use History in Type Annotations"] Calls["Call Accessors to Read Data"] end
TypeDecl --> PubType Accessors --> PubFuncs PubType --> Annotations PubFuncs --> Calls ```
[18:54](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1134s) Preventing Breaking Changes with Accessor Functions
In a typical Elm module header, exporting a custom type along with all its constructors is done using the syntax `History(..)`, exposing what Feldman calls an “entire pantheon of one constructor.” When authors expose both the type and the constructor, consumers can freely pattern match and reach directly into the underlying representation. However, if the module author only exports `History` (without the `(..)`), consumers can use the name `History` in their type annotations, but they cannot run a `case` expression on it or construct it manually. Feldman explains the enforcement bluntly: “sorry but you can't you don't have access to that internal to our own module we can always do that because we've defined it right here in our own module but other modules no I'm afraid they're just not going to have access to that.”
This restriction raises the question of how an outside module reads data, such as accessing the list of questions. The answer is to expose explicit accessor functions: - `questions: History -> List Question` - `current: History -> Question`
By exposing accessors instead of direct record fields, library authors decouple the public interface from the internal storage mechanism. Consumers get the data structure most convenient for rendering (a plain list of questions) without having to deal with the zipper list themselves. Most importantly, maintainers can swap the internal implementation from a list with an index to a zipper list without causing a breaking change for external users.
[21:25](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1285s) Modeling Mutually Dependent State: The Status Bar
Feldman moves to a final practical example: adding a notification status bar to the survey editor. When a user creates a question, the interface shows a message like “Question created” accompanied by a trumpet icon. When a question is deleted, modern UX conventions dictate offering an “Undo” button alongside the status text so users can recover from accidental clicks.
To support this undo behavior, the application needs to retain the deleted question. A naive model might look like this:
```elm type alias Model = { status: Maybe String , questionToRestore: Maybe Question } ```
This design introduces invalid state combinations: - `status = Just "Question created"`, `questionToRestore = Nothing` (Valid) - `status = Just "Question deleted"`, `questionToRestore = Just question` (Valid) - `status = Nothing`, `questionToRestore = Just question` (Invalid: an undo payload exists, but no status message is visible to provide an undo button)
As Feldman notes, “What if status is nothing but we somehow have something to restore? What does that mean? That seems like again something went wrong somewhere we had we've got a bug on our logic um this should be impossible.” The fix is replacing the two independent `Maybe` fields with a single domain-specific custom type:
```elm type Status = NoStatus | TextStatus String | DeletedStatus String Question ```
This custom type encodes the rules directly into the Elm type system: deleted questions cannot exist without an accompanying status string, and a clean interface (`NoStatus`) cannot secretly hold leftover data.
[23:38](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1418s) Conclusion and Rules of Thumb for Model Design
Feldman summarizes the core design principles explored throughout the talk into three guiding questions for application architecture:
1. **Two `Maybe`s vs. a tailored union type**: Feldman admits that coming from a JavaScript background, his natural instinct used to be simply adding another `Maybe` whenever new optional data appeared. However, “very often when I've taken another second to think about it I'm like oh actually there's going to be some combination of those two maybe that's not going to do what I want it it'll actually be better if I switch from one maybe to a union type instead of adding a second maybe and this gets more and more true as the application gets bigger.” 2. **Two separate lists vs. one list with enriched elements**: Introducing a second list often causes out-of-sync bugs. Instead, keep a single list and attach the additional metadata to the individual elements. 3. **Revising implementations without breaking builds**: Once implementation details are exposed to downstream users, breaking changes become unavoidable during refactors. Module authors should hide constructors by default and provide stable accessors from the initial release.
He concludes with a final plea to the audience: “So if it's possible to represent states that should be impossible, please at least consider making the possible impossible. Thanks very much.”
## Notable quotes > [06:22](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=382s) tests are good, tests are good, but impossible is better. — Richard Feldman
> [23:38](https://www.youtube.com/watch?v=IcgmSRJHu_8&t=1418s) So if it's possible to represent states that should be impossible, please at least consider making the possible impossible. — Richard Feldman
## Mentioned Richard Feldman, Evan Czaplicki, Murphy Randle, Tessa Kelly, elm-css, Elm, elm-conf, CSS, JavaScript
## Video description Among the most time-consuming bugs to track down are the ones where we look at our application state and say "this shouldn’t be possible."
We can use Elm’s compiler to rule out many of these bugs in the first place—but only if we design our Models using the right techniques! This talk explores how.
[Source](https://www.youtube.com/watch?v=IcgmSRJHu_8)