Eric Normand's Newsletter

Each week, enjoy a thoughtful essay in your inbox, focused on the following topics:

  • software design
  • functional programming
  • software engineering practices

I believe our industry needs more rigor when talking about how to write better software. At the same time, we also need more speech from the heart. I hope you enjoy the combination of the empirical and personal.

Do any of these describe you?

Do any of these describe you?

  • You're a thoughtful programmer.
  • You enjoy reading reviews of Alan Kay's lectures.
  • You want to look beyond today's trends to improve your craft.
  • You want to understand the history of your field.
  • You like staring intensely into a problem.

If so, then this newsletter is for you.

The older archives of the newsletter are below. The new posts will appear on Substack. You may wish to use the Substack interface to manage your subscription.

Join thousands of happy subscribers

Here are just a few happy comments:

Even if you’re not interested in Clojure at all, this newsletter is an excellent read every week.

Oliver CaldwellOliver Caldwell

Oliver Caldwell

A free, weekly email to inspire Clojure programmers :D

Daniel HigginbothamDaniel Higginbotham

Daniel Higginbotham

If you’re a Clojure programmer, you should subscribe to the (free) newsletter by Eric Normand.

Steve MinerSteve Miner

Steve Miner

Your friendly reminder that if you aren’t reading Eric's newsletter, you are missing out… NOTE: It's not Clojure specific!

Sean AllenSean Allen

Sean Allen

BTW, if you read RubyWeekly, you’ll love Eric Normand's newsletter.

Norbert WójtowiczNorbert Wójtowicz

Norbert Wójtowicz

Eric's newsletter is so simply great. Love it!

Mathieu GagnonMathieu Gagnon

Mathieu Gagnon

Read it and weep (with joy)

Alan ShawAlan Shaw

Alan Shaw

Lots of great content in the latest newsletter! Really glad I subscribed. Thanks, Eric, for your work.

Nicolas HeryNicolas Hery

Nicolas Hery

Past issues

📬 Recent Issues Substack Archive

New posts will appear on Substack.

Eric Normand Newsletter 479: OOP, FP, and the Future

OOP, FP, and the Future

Eric Normand Newsletter 478: Self-publishing Domain Modeling 📖

Self-publishing Domain Modeling 📖

Eric Normand Newsletter 477: Encapsulating collections

Encapsulating collections

Eric Normand Newsletter 476: The start of a book

The start of a book

Eric Normand Newsletter 475: Transcending OOP and FP

Transcending OOP and FP

Eric Normand Newsletter 474: Data-Oriented Programming

Data-Oriented Programming

Eric Normand Newsletter 473: On the meanings of software design

On the meanings of software design

Eric Normand Newsletter 472: Working on Domain Modeling book

Working on Domain Modeling book

Eric Normand Newsletter 471: I’m back!

I’m back!

Eric Normand Newsletter 470: Worked Example: Days of the week

Worked Example: Days of the week

Eric Normand Newsletter 469: Everything is a center

Everything is a center

Eric Normand Newsletter 468: Choice of abstraction matters

Choice of abstraction matters

Eric Normand Newsletter 467: Koppel's abstractions

Koppel's abstractions

Eric Normand Newsletter 466: You need two abstractions to model

You need two abstractions to model

Eric Normand Newsletter 465: Abstraction is the essence of programming

Abstraction is the essence of programming

Eric Normand Newsletter 464: 10 years!

10 years! Newsletter 463: What is beautiful code? Newsletter 462: Migration Newsletter 461: Rules of thumb don't scale Newsletter 460: Interface polymorphism Newsletter 459: Revisiting the open/closed principle Newsletter 458: Three doorways of domain modeling Newsletter 457: Take a stance Newsletter 456: A sense of mystery Newsletter 455: How and when to apply domain modeling Newsletter 454: Not a prescriptive process Newsletter 453: Model as Toy Newsletter 452: Domain Invariants Newsletter 451: Signature-Driven Development Newsletter 450: Runnable specifications Newsletter 449: Domain model correctness and simplicity Newsletter 448: Domain model convenience Newsletter 447: Domain model fit Newsletter 446: The Art of Domain Modeling Newsletter 445: 2 Kinds of genericity? Newsletter 444: Humane models of errors Newsletter 443: Simulating and visualizing time Newsletter 442: Human interfaces should be rich and composed Newsletter 441: Hurricane update Newsletter 440: Pub/sub done really well Newsletter 439: How to invent the future Newsletter 438: The unbearable lightness of programming Newsletter 437: To build or not to build, that is the question Newsletter 436: Software as Soulcraft Newsletter 435: The software crisis Newsletter 434: Re-combination of parts Newsletter 433: The "Clojure Effect" Newsletter 432: Specific vs. general Newsletter 431: Clojure directness Newsletter 430: Use :: keyword notation for unique values Newsletter 429: CRUD Authorization Newsletter 428: CRUD Paging Newsletter 427: CRUD Handlers Newsletter 426: Ring for CRUD Newsletter 425: Specify a data abstraction Newsletter 424: Leverage in a thriving ecosystem Newsletter 423: Grokking Simplicity is in print! Newsletter 422: Don't write your own language Newsletter 421: Programming monism Newsletter 420: Say what you mean Newsletter 419: Why not write your own language? Newsletter 418: Clojure's buzz Newsletter 417: Clojure at heart 🥰 Newsletter 416: Why do we program in hard mode? Newsletter 415: What is worth sacrificing clarity? Newsletter 414: Constrain your design with composition first Newsletter 413: Affordances in software Newsletter 412: use and abuse of the decorator pattern Newsletter 412: module depth is bogus Newsletter 411: is it easier after Clojure? Newsletter 410: don't encode your policy Newsletter 409: the 3-stakeholders model Newsletter 408: 3 stakeholders of programming Newsletter 407: two layers of design Newsletter 406: design, false nominalization? Newsletter 405: giving thanks Newsletter 404: where to find timeless truths Newsletter 403: the timeless in an unstable domain Newsletter 402: up-front vs incremental design Newsletter 401: design is not about ease of change Newsletter 400: is software design worthless? Newsletter 399: is more layers better? Newsletter 398: semantics over syntax Newsletter 397: unknown keys in maps Newsletter 396: how types help you reason Newsletter 395: types depend on context Newsletter 394: on the feedback of types Newsletter 393: always start with a repl Newsletter 392: command your repl Newsletter 391: the immediacy interval Newsletter 390: the elements of flow in a REPL environment Newsletter 389: repl withdrawal Newsletter 388: further down (or is it up?) the stack Newsletter 387: to stack or not to stack Newsletter 386: factoring equations in code Newsletter 385: two map patterns: entity vs index Newsletter 384: name your callbacks Newsletter 383: replace body with callback Newsletter 382: express implicit argument Newsletter 381: #BlackLivesMatter Newsletter 380: what can you iterate through? Newsletter 379: get produces unexpected behavior, as expected Newsletter 378: Clojure nudges us to constant-time operations Newsletter 377: Three states of key-value pairs Newsletter 376: Learn from core Newsletter 375: JS vs CLJ Newsletter 374: Seek truth, not convenience Newsletter 373: Don't sweat the stack Newsletter 372: Model change over time with state machines Newsletter 371: Chain map, filter, and reduce Newsletter 370: Refactoring: replace get, modify, set with update Newsletter 369: Refactoring: replace body with callback Newsletter 368: Refactoring: extract calculation from action Newsletter 367: What about errors? Newsletter 366: Is the Clojure philosophy bad for beginners? Newsletter 365: why not more convenience routines for CSV parsing? Newsletter 364: Tip: seek the model and enshrine it in code Newsletter 363: Learn to build and deploy a single-page application Newsletter 362: Tip: double recursion Newsletter 361: Tip: trampoline your tail recursion Newsletter 360: Tip: fold left vs fold right Newsletter 359: Tip: reduce as universal recursion over a list Newsletter 358: Tip: recursion rollercoaster Newsletter 357: Tip: recursion vs iteration Newsletter 356: Tip: convert recursion to iteration Newsletter 355: Tip: memoize to trade space for time Newsletter 354: Tip: beware how many threads you start Newsletter 353: Tip: String literals are interned Newsletter 352: Tip: use the right kind of comment for the job Newsletter 351: Clojure tool: babashka Newsletter 350: Monoid pattern submissions Newsletter 349: Tip: monoid pattern Newsletter 348: Tip: cleanup in finally Newsletter 347: Tip: catch and catch again Newsletter 346: Tip: just throw ex-info Newsletter 345: Tip: know your exception hierarchy Newsletter 344: Tip: thank a Clojure OSS dev today Newsletter 343: Tip: read the exception message Newsletter 342: Tip: learn to read Javadocs Newsletter 341: Tip: tidy up your ns form Newsletter 340: Fewer side-effects is better than more Newsletter 339: Design is about pulling things apart Newsletter 338: Tip: Re-implement multiple times Newsletter 337: Functional programming is deep Newsletter 336: My book: next week? Newsletter 335: Idea: when can you use property-based testing? Newsletter 334: Tip: can you fill in the blanks? Newsletter 333: Tool: rebel readline Newsletter 332: Tool: jEnv Newsletter 331: Tool: shadow-cljs Newsletter 330: Tool: Figwheel Newsletter 329: Tool: cljdoc Newsletter 328: Tip: don't use def inside a defn Newsletter 327: Tip: always be decomplecting Newsletter 326: Tip: consult the repl Newsletter 325: Tip: don't use a protocol to make testing easier Newsletter 324: Tip: Use tuples for local data, entities for global data Newsletter 323: Tip: Keep your workflow simple Newsletter 322: Tip: Avoid flatten when possible Newsletter 321: Tip: Do not count sequences from strangers Newsletter 320: Tip: Know Clojure's execution semantics for better Repl-Driven Development Newsletter 319: Tip: Shebang scripting with the Clojure CLI Newsletter 318: Tip: Beware the order of keys in hashmaps Newsletter 317: When you see a job, apply Newsletter 316: Avoid licensing and support issues with the right JDK for you Newsletter 315: Use the correct data structure for the job Newsletter 314: Collection functions vs. sequence functions Newsletter 313: Always use the 3-argument version of reduce Newsletter 312: Maybe, Arcadia, Improvements Newsletter 311: Stewardship, Trees, Databases Newsletter 310: Quines, REPLs, HTML forms Newsletter 309: Hiring, cljdoc, Probability Newsletter 308: Conferences, Unison, NULL Newsletter 307: Performance, Architecture, Semantics Newsletter 306: Error Messages, Milky Way, Microservices Newsletter 305: Legacy, Tables, Pioneers Newsletter 304: Back to the factory Newsletter 303: The Clojure Process Debate Newsletter 302: The Agile Software Factory Newsletter 301: GraphQL, Hiccup, Symbolic Execution Newsletter 300: Rust, React, Newsletter 299: Fullstack, ClojureScript, Class hierarchies Newsletter 298: UX, Deliverables, Dependent Types Newsletter 297: Beginner Experience Edition Newsletter 296: Design, Success, and Conj Speakers Newsletter 295: Deps, Categories, Dystopia Newsletter 294: Ions, Elm, Reason Newsletter 293: Design, deps.edn, Type Inference Newsletter 292: Composition, Modeling, Scope Newsletter 291: Metaphor, Puzzles, Ions Newsletter 290: Generative, Non-events, Hammocks Newsletter 289: More Spec, Fewer Classes, Threading Macros Newsletter 288: Clojure Spec Tools Newsletter 287: DataScript, GraphQL, CRDTs Newsletter 286: Broken APIs, CIDER, Immutability Newsletter 285: Scraping, Laziness, Design Patterns Newsletter 284: Threading, React, OTP Newsletter 283: Spec, Error messages!, Games Newsletter 282: Careers, Errors, P2P Newsletter 281: Intelligence Augmentation, Complexity, Game Engines Newsletter 280: Datomic Ions, Costly Code, Monad drama Newsletter 279: Clojure web frameworks Newsletter 278: Parinfer, Naming, GraphQL Newsletter 277: Clojure Career Workshop Newsletter 276: Turing, Bottom-up Design, go blocks Newsletter 275: Data, Spec, Types Newsletter 274: Datomic, Strange Loop, Clojurists Together Newsletter 273: Robots, Categories, Aikido Newsletter 272: Did you see the redesign? Newsletter 271: Falsehoods, Deliverability, cljs.main Newsletter 270: Lisp and the metacircular interpreter Newsletter 269: Elements, Domains, Revenge! Newsletter 268: Apropos, Datomic, Serverless Newsletter 267: Debugging, REPLs, Theory Newsletter 266: Beginner Experience Newsletter 265: The one after the first Clojure SYNC Newsletter 264: Happy Lundi Gras Newsletter 263: The Power of Definitions Newsletter 262: Launching, Jargon, Fearless Newsletter 261: Cool, Calm, Coast Newsletter 260: Orchestra, Logic, Lambda Newsletter 259: Smalltalk, Slowness, Learning Newsletter 258: Reality, Hiccup, Relational Newsletter 257: Dynamic, Benchmarks, Design Patterns Newsletter 256: Jobs, SYNC, Patterns Newsletter 255: Scene, CLI, Neural Net Newsletter 254: Calm, Microservices, Complexity Newsletter 253: Poe, Cookies, Flash Newsletter 252: Brand, Distributed, Feelings Newsletter 251: Machine Learning, ClojureScript, The Future Newsletter 250: Lean, Creole Cuisine, Types Newsletter 249: The unbearable lightness of static type erasure... Newsletter 248: Weapons, GraphQL, and the return of an old acquaintance Newsletter 247: Clojure SYNC, Guy Steele, Zach Tellman Newsletter 246: 2 CFPs, Clojurecademy, Data Science Newsletter 245: V2, Scale, Perl Newsletter 244: Comics, Infinity, Scaling Newsletter 243: Specialization, McLuhan, Lumo Newsletter 242: Flexibility, Measurement, Metaphors Newsletter 241: Paradigms, Data Science, Style Newsletter 240: The revolution will not be computerized. Newsletter 239: Clojure SYNC tickets on sale Newsletter 238: History, Abstraction, Play Newsletter 237: Unicycles, Evolution, Chasm Newsletter 236: Contrarians, Software, Side Projects Newsletter 235: 10x Programmers, Types and Design, Deep Learning Newsletter 234: 50% off, Teams, War Newsletter 233: Parasites, Carpenters, Re-frame Newsletter 232: AI, Jobs, Batman Newsletter 231: Luna, Beautiful, Abstraction Newsletter 230: Brutalism, Delays, Characters Newsletter 229: Concurrency, Declarative, Addiction Newsletter 228: Klipse, Cortex, Performance Newsletter 227: Drama Edition Newsletter 226: Hacking+Ruby+Clojure Newsletter 225: Spec, Deep Learning, New Orleans Newsletter 224: Dune, Leiningen, CIDER Newsletter 223: The Clojure Density of the Universe Newsletter 222: Laziness, core.async, and Re-frame Newsletter 221: Trampolines, Typing, and Feeling Good Newsletter 220: Re-frame Components Newsletter 219: Clojure/West Newsletter 218: Lisp and Flow Newsletter 217: Re-frame Newsletter 216: ClojureScript vs JavaScript Newsletter 215: Clojure and Generative Testing Newsletter 214: JVM Fundamentals for Clojure Sale Begins Newsletter 213: Clojure and the JVM Newsletter 212: Clojure's simple bravery Newsletter 211: Collaborative Clojuring, SICP, Union Types Newsletter 210: CLJS: Externs inference, Why CLJS?, Live editor Newsletter 209: Peter Landin and Christopher Alexander Newsletter 208: Alan Kay Newsletter 207: Guy Steele, Jr. Newsletter 205: Parallelism, Reducers, Datomic Newsletter 204: Artificial, Mutants, Causal Laws Newsletter 203: Video Deluge, Salaries, JVM, Clojure Remote Newsletter 202: The Clojure/conj edition Newsletter 201: Consulting, Wizards, Looking Glass Newsletter 200: Patterns, Bell Labs, Aliens

Clojure Gazette 199: Clockwork, Personal Computing, Fun

Clojure Gazette 198: Alan Kay, The Matrix, John Hughes

Clojure Gazette 197: core.match, DSLs, and functional salaries

Clojure Gazette 196: We've got links again!

Clojure Gazette 195: The next conference frontier

Clojure Gazette 194: OOP: Why the bashing?

Clojure Gazette 193: Where are the good FP books?

Clojure Gazette 192: Composition is about Decomposing

Clojure Gazette 191: Where does Clojure shine?

Clojure Gazette 190: Reusability and Composition

Clojure Gazette 189: Expertise sounds mystical

Clojure Gazette 188: Separation of concerns

Clojure Gazette 187: Who won the language wars?

Clojure Gazette 186: How Small Abstractions Help Beginners

Clojure Gazette 185: What is Functional Programming?

Clojure Gazette 184: The Onslaught of States

Clojure Gazette 183: The Magic of Abstraction

Clojure Gazette 182: The Joy of Programming to Learn

Clojure Gazette 181: Leaps of Abstraction

Clojure Gazette 180: "How do you structure your apps?"

Clojure Gazette 179: Trade user stories for hammock time

Clojure Gazette 178: The Biggest Waste in Our Industry

Clojure Gazette 177: The Hidden Costs of Abstraction

Clojure Gazette 176: The Ultimate Abstraction

Clojure Gazette 175: Can larger abstractions help more than they hurt?

Clojure Gazette 174: Deepening the tree

Clojure Gazette 173: Dealers of Abstraction

Clojure Gazette 172: Comparing designs from different contexts

Clojure Gazette 171: The Structure of Structure

Clojure Gazette 170: Continuous Evolution

Clojure Gazette 169: Web, Art, Nomads

Clojure Gazette 168: Podcasts and Statistics

Clojure Gazette 167: Cider, Jepsen, Naid

Clojure Gazette 166: Dead code, Bias, Books

Clojure Gazette 165: Newbie, Open Source, Tufte

Clojure Gazette 164: Simplicity, Lists, Jobs

Clojure Gazette 163: Conference, REPL, Tuples

Clojure Gazette 162: Grace Hopper, Fork, Lisp

Clojure Gazette 161: Design Patterns, ClojureScript, Learning

Clojure Gazette 160: Om Next, Fairy Tales, Lambda

Clojure Gazette 159: Overtone, Boot, Specter

Clojure Gazette 158: Survey, Mystics, Embodiment

Clojure Gazette 157: Om Next, Opportunity Grants, Refinement Types

Clojure Gazette 156: Martin Luther King, Microservices, Naming

Clojure Gazette 155: Labor, Internet, Datomic Newsletter 206: Fractals, Reagent, McCarthy

Clojure Gazette 154: Parentheses, Interactivity, Speaking

Clojure Gazette 153: Survey, CIDER, Om Next

Clojure Gazette 152: Static, Dynamic, Business

Clojure Gazette 151: Top Down, Emacs, CQRS

Clojure Gazette 150: Creativity, Runtime, Documentation

Clojure Gazette 149: London, Om/Next, CIDER

Clojure Gazette 148: Performance, Learning, Eventual Consistency

Clojure Gazette 147: Evidence, Denotation, Alan Kay

Clojure Gazette 146: Refactoring, Structural Typing, Clojure Remote

Clojure Gazette 145: Subversive thoughts inside.

Clojure Gazette 144: Can manipulating deep data structures be faster than update-in?

Clojure Gazette 143: Strange Loop, Activism, Typed Clojure

Clojure Gazette 142: Hoplon, React Native, Lisp-Flavored Erlang

Clojure Gazette 141: Spam, Transit, Modules

Clojure Gazette 140: Devcards, Smalltalk, React Native

Clojure Gazette 139: core.async, Haskell, Pairing

Clojure Gazette 138: Tenure, Adventure, Zines!

Clojure Gazette 136: Typed Clojure Core Annotations

Clojure Gazette 135: Transients with Core Typed

Clojure Gazette 134: Custom documentation for Clojure

Clojure Gazette 133: TDD in Clojure on Android

Clojure Gazette 132: Integrating ClojureScript with the Javascript ecosystem

Clojure Gazette 137: Knuth, Om, Objects

Clojure Gazette 131: YAGNI, LaTeX, React

Clojure Gazette 130: iOS, Types, Web

Clojure Gazette 129

Clojure Gazette 128

Clojure Gazette 127

Clojure Gazette 126

Clojure Gazette 125

Clojure Gazette 124

Clojure Gazette 123

Clojure Gazette 122

Clojure Gazette 121

Clojure Gazette 120

Clojure Gazette 119: Continuous Integration Special

Clojure Gazette 118

Clojure Gazette 117

Clojure Gazette 116

Clojure Gazette 115

Clojure Gazette 114

Clojure Gazette 113

Clojure Gazette 112

Clojure Gazette 111

Clojure Gazette 110

Clojure Gazette 109

Clojure Gazette 108

Clojure Gazette 107

Clojure Gazette 106

Clojure Gazette 105

Clojure Gazette 104

Clojure Gazette 103

Clojure Gazette 102

Clojure Gazette 101

Clojure Gazette 100

Clojure Gazette 1.99

Clojure Gazette 1.98

Clojure Gazette 1.97

Clojure Gazette 1.96

Clojure Gazette 1.95

Clojure Gazette 1.94

Clojure Gazette 1.93

Clojure Gazette 1.92

Clojure Gazette 1.91

Clojure Gazette 1.90

Clojure Gazette 1.89

Clojure Gazette 1.88

Clojure Gazette 1.87

Clojure Gazette 1.86

Clojure Gazette 1.85

Clojure Gazette 1.84

Clojure Gazette 1.83

Clojure Gazette 1.82

Clojure Gazette 1.81

Clojure Gazette 1.80

Clojure Gazette 1.79

Clojure Gazette 1.78

Clojure Gazette 1.77

Clojure Gazette 1.76

Clojure Gazette 1.75

Clojure Gazette 1.74

Clojure Gazette 1.73

Clojure Gazette 1.72

Clojure Gazette 1.71

Clojure Gazette 1.70

Clojure Gazette 1.69

Clojure Gazette 1.68

Clojure Gazette 1.67

Clojure Gazette 1.66

Clojure Gazette 1.63

Clojure Gazette 1.62

Clojure Gazette 1.61

Clojure Gazette 1.60

Clojure Gazette 1.59

Clojure Gazette 1.58

Clojure Gazette 1.57

Clojure Gazette 1.56

Clojure Gazette 1.55

Clojure Gazette 1.54

Clojure Gazette 1.53

Clojure Gazette 1.52

Clojure Gazette 1.50

Clojure Gazette 1.51

Clojure Gazette 1.49

Clojure Gazette 1.48 (correction)

Clojure Gazette 1.48

Clojure Gazette 1.47

Clojure Gazette 1.46

Clojure Gazette 1.45

Clojure Gazette 1.44

Clojure Gazette 1.43

Clojure Gazette 1.42

Clojure Gazette 1.41

Clojure Gazette 1.40

Clojure Gazette 1.39

Clojure Gazette 1.38

Clojure Gazette 1.37

Clojure Gazette 1.36

Clojure Gazette 1.35

Clojure Gazette 1.34

Clojure Gazette 1.33

Clojure Gazette 1.32

Clojure Gazette 1.31

Clojure Gazette 1.30

Clojure Gazette 1.29

Clojure Gazette 1.65

Clojure Gazette 1.28

Clojure Gazette 1.64

Clojure Gazette 1.27

Clojure Gazette 1.26

Clojure Gazette 1.25

Clojure Gazette 1.24

Clojure Gazette 1.23

Clojure Gazette 1.22

Clojure Gazette 1.21

Clojure Gazette 1.20

Clojure Gazette 1.19

Clojure Gazette 1.18

Clojure Gazette 1.17

Clojure Gazette 1.16

Clojure Gazette 1.15

Clojure Gazette 1.14

Clojure Gazette 1.13

Clojure Gazette 1.12

Clojure Gazette 1.11

Clojure Gazette 1.10

Clojure Gazette 1.9

Clojure Gazette 1.8

Clojure Gazette 1.7

Clojure Gazette 1.6

Clojure Gazette 1.5

Clojure Gazette 1.4

Clojure Gazette 1.3

Clojure Gazette 1.2

Clojure Gazette 1.1