Skip to main content

· 65 min read
1. Teach Yourself Programming in Ten Years 001 2. No Silver Bullet: Essence and Accidents of Software Engineering 002 3. Programming as Theory Building 003 4. The Law of Leaky Abstractions 004 5. Things You Should Never Do, Part I 0056. Out of the Tar Pit 006 7. You and Your Research 007 8. The Rise of 'Worse is Better' 008 9. Reflections on Trusting Trust 009 10. How Complex Systems Fail 01011. Choose Boring Technology 011 12. The Grug Brained Developer 012 13. The Wrong Abstraction 013 14. Parse, Don't Validate 014 15. End-to-End Arguments in System Design 01516. A Note on Distributed Computing 016 17. On the Criteria To Be Used in Decomposing Systems into Modules 017 18. The Humble Programmer 018 19. Go To Statement Considered Harmful 019 20. Why Functional Programming Matters 02021. Big Ball of Mud 021 22. CAP Twelve Years Later: How the 'Rules' Have Changed 022 23. Life Beyond Distributed Transactions: An Apostate's Opinion 023 24. Building on Quicksand 024 25. Immutability Changes Everything 02526. The Log: What every software engineer should know about real-time data's unifying abstraction 026 27. Scalability! But at what COST? 027 28. The Tail at Scale 028 29. Ironies of Automation 029 30. An Investigation of the Therac-25 Accidents 03031. They Write the Right Stuff 031 32. MonolithFirst 032 33. Is High Quality Software Worth the Cost? 033 34. Mocks Aren't Stubs 034 35. Microservices 03536. Test Pyramid 036 37. Is TDD dead? 037 38. The Joel Test: 12 Steps to Better Code 038 39. Fire and Motion 039 40. Back to Basics 04041. The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) 041 42. What Color is Your Function? 042 43. C Is Not a Low-level Language 043 44. What Every Programmer Should Know About Memory 044 45. The Lost Art of C Structure Packing 04546. What Every Computer Scientist Should Know About Floating-Point Arithmetic 046 47. Execution in the Kingdom of Nouns 047 48. Falling Into The Pit of Success 048 49. Semantic Compression 049 50. Learnable Programming 05051. Magic Ink: Information Software and the Graphical Interface 051 52. Responsive Web Design 052 53. A Dao of Web Design 053 54. Cool URIs don't change 054 55. The Web's Grain 05556. The Website Obesity Crisis 056 57. Web Design: The First 100 Years 057 58. The Internet With a Human Face 058 59. The Software Disenchantment 059 60. Falsehoods Programmers Believe About Names 06061. Falsehoods programmers believe about time 061 62. Programming Sucks 062 63. Local-first software: You own your data, in spite of the cloud 063 64. Hyrum's Law 064 65. Cargo Cult Software Engineering 06566. Software Quality at Top Speed 066 67. On Being a Senior Engineer 067 68. Being Glue 068 69. Staff Engineer: Archetypes 069 70. The Engineer/Manager Pendulum 07071. Maker's Schedule, Manager's Schedule 071 72. Don't Call Yourself A Programmer, And Other Career Advice 072 73. Salary Negotiation: Make More Money, Be More Valued 073 74. Beating the Averages 074 75. Hackers and Painters 07576. Taste for Makers 076 77. How to Get Startup Ideas 077 78. Do Things that Don't Scale 078 79. Startup = Growth 079 80. Schlep Blindness 08081. Keep Your Identity Small 081 82. The Top Idea in Your Mind 082 83. How to Do Great Work 083 84. Good and Bad Procrastination 084 85. Great Hackers 08586. How to Work Hard 086 87. How to Be Polite 087 88. Ten Lessons I Wish I Had Been Taught 088 89. Solitude and Leadership 089 90. The Bitter Lesson 09091. Why Software Is Eating the World 091 92. A little bit of plain JavaScript can do a lot 092 93. Choices 093 94. Application compatibility layers are there for the customer, not for the program 094 95. John Carmack on Inlined Code 09596. Rob Pike's Rules of Programming 096 97. Design Principles Behind Smalltalk 097 98. The Error Model 098 99. Structured Programming with go to Statements 099 100. The Early History of Smalltalk 100

Article notes

001 — Teach Yourself Programming in Ten Years — Peter Norvig

You will not become a programmer by absorbing syntax from a crash course; the craft develops through years of deliberate practice. Build substantial programs, study other people's code, work with programmers both stronger and weaker than you, and learn languages from several paradigms so that each changes how you think. Keep stretching just beyond your current ability, seek feedback, and understand the machine beneath your abstractions.

002 — No Silver Bullet: Essence and Accidents of Software Engineering — Frederick P. Brooks Jr.

Separate the accidental difficulties of tools and notation from the essential difficulty of constructing precise conceptual systems. Better languages, environments, and hardware can remove friction, but no single invention can erase the complexity, conformity, changeability, and invisibility inherent in software. Improve by buying rather than rebuilding, prototyping requirements, growing systems incrementally, and cultivating unusually strong designers.

003 — Programming as Theory Building — Peter Naur

Regard a program as the visible residue of a theory held by the people who built it: an understanding of how the problem, its constraints, and the chosen solution fit together. Source code and documentation alone cannot fully preserve that theory, which is why maintenance becomes brittle when no one who understands the original reasoning remains. Preserve software by transferring understanding through close collaboration, explanation, and continued participation—not merely by preserving artifacts.

004 — The Law of Leaky Abstractions — Joel Spolsky

Abstractions make routine work easier, but their underlying machinery still surfaces through failures, performance limits, and unusual cases. A network API may resemble a local call until latency or disconnection matters; a database may hide indexes until a query becomes slow. Learn at least one layer below the interface you use, because abstractions save labor without eliminating the knowledge needed to diagnose reality.

005 — Things You Should Never Do, Part I — Joel Spolsky

Resist replacing a mature codebase merely because the existing structure feels ugly. Every awkward line may encode a bug fix, performance lesson, or edge case accumulated through real use, and a rewrite silently discards that knowledge while freezing visible progress. Improve the system incrementally, preserving behavior as you reshape it, unless you can justify the exceptional cost and risk of starting over.

006 — Out of the Tar Pit — Ben Moseley and Peter Marks

Attack complexity by reducing mutable state and the control logic that determines when state changes. Separate essential domain data and rules from accidental mechanisms, express derived values declaratively, and keep the remaining state visible and constrained. A functional core paired with a relational model makes behavior easier to understand because fewer facts depend on execution history.

007 — You and Your Research — Richard Hamming

Your choice of problem determines much of what your work can become, so look for questions that are both consequential and open to attack. Develop courage, tolerate ambiguity, protect sustained thinking time, and communicate results clearly enough that others can use them. Luck matters, but preparation, working conditions, persistence, and the choice of problem determine how often luck can help you.

008 — The Rise of Worse is Better — Richard P. Gabriel

Favor designs that are simple to implement, ship, and port when that simplicity lets a system spread and improve through use. A theoretically cleaner design can lose to one that places fewer demands on implementers, even when the latter compromises interface consistency or completeness. Judge elegance alongside adoption dynamics: a small, viable system can acquire quality, while an immaculate system that never propagates cannot.

009 — Reflections on Trusting Trust — Ken Thompson

Follow the chain of trust below source code and notice that every compiler, assembler, loader, and piece of microcode can influence the program you finally run. A compiler can learn to insert a backdoor into both a login program and future versions of itself, leaving no trace in the inspected source. Treat software assurance as a supply-chain and provenance problem, not something source review alone can settle.

010 — How Complex Systems Fail — Richard I. Cook

A complex-system failure is rarely the work of one careless operator or one isolated root cause. Reconstruct the path through latent weaknesses, defenses, adaptations, and production pressures from what practitioners could see before the outcome was known. Strengthen the system's capacity to adapt and recover, remembering that people continuously create safety while operating an already imperfect system.

011 — Choose Boring Technology — Dan McKinley

Spend novelty deliberately: every unfamiliar database, language, or framework adds operational unknowns and consumes a team's finite ability to learn. Prefer mature components whose limitations and failure modes are already understood, then introduce new technology only when it solves a problem the existing stack genuinely cannot. Count the interactions among choices as part of their cost, because complexity grows faster than the component list.

012 — The Grug Brained Developer — Carson Gross

Protect your limited working memory from complexity, the predator that grows quietly as abstractions, distributed state, and clever machinery accumulate. Start with straightforward code, introduce abstractions only when repeated evidence earns them, and keep boundaries narrow enough to understand locally. Use tests, types, tools, and factoring as aids to comprehension rather than ceremonies that create more concepts than they remove.

013 — The Wrong Abstraction — Sandi Metz

A shared abstraction stops helping when it must serve several cases that change for different reasons. Restore the duplicated implementations, make each correct in isolation, and let the true common structure reveal itself over time. Duplication is cheaper than a false abstraction because it keeps future changes local and leaves you free to discover a better boundary.

014 — Parse, Don’t Validate — Alexis King

Convert untrusted input into a type that carries the facts you have established instead of checking a weak value and then forgetting the result. Parsing moves failure to the system boundary and lets downstream functions accept only valid states, replacing defensive rechecks with stronger interfaces. Choose data structures that preserve evidence—such as a non-empty list rather than a list separately asserted to be non-empty—so correctness follows from construction.

015 — End-to-End Arguments in System Design — Jerome H. Saltzer, David P. Reed, and David D. Clark

Place a correctness function at the endpoints when only the application can know whether that function has truly succeeded. Lower layers may improve reliability or performance, but checks such as complete file transfer still require end-to-end verification because intermediate guarantees cannot cover every failure. Keep the shared substrate simple, and add lower-level mechanisms only when their broad performance benefit justifies the cost.

016 — A Note on Distributed Computing — Jim Waldo, Geoff Wyant, Ann Wollrath, and Sam Kendall

A remote interaction is not an ordinary local object call, however similar the interfaces appear. Latency, partial failure, concurrency, memory access, and object lifetime make distribution a semantic boundary that applications must confront explicitly. Design interfaces around coarse-grained communication and failure-aware behavior, because pretending location is transparent produces systems that are fragile in precisely the cases that matter.

017 — On the Criteria To Be Used in Decomposing Systems into Modules — David L. Parnas

Divide a system around design decisions likely to change, hiding each decision behind a stable interface. A pipeline-shaped decomposition that mirrors processing steps may look orderly yet force many modules to know the same representation details. Encapsulate secrets such as storage layout and algorithm choice so teams can work independently, changes remain localized, and readers can understand one module without reconstructing the whole system.

018 — The Humble Programmer — Edsger W. Dijkstra

Accept that software can exceed the unaided mind's capacity long before it exceeds the computer's. Control that gap through languages, proofs, and structures that let you reason about behavior in compact, composable units instead of relying on testing and cleverness alone. Approach programming with humility: simplify the intellectual task until correctness becomes tractable.

019 — Go To Statement Considered Harmful — Edsger W. Dijkstra

A computation is easiest to reason about when its progress remains aligned with the structure of its text. Unrestricted jumps create arbitrary execution coordinates, forcing a reader to reconstruct history before understanding the current state. Prefer control constructs with constrained entry and exit points so program behavior remains accessible to reasoning and proof.

020 — Why Functional Programming Matters — John Hughes

Treat higher-order functions and lazy evaluation as mechanisms for modularity, not ornamental language features. Higher-order functions separate reusable patterns of computation, while laziness lets producers and consumers be composed without committing to an execution schedule or intermediate structure. Build programs from independently useful pieces whose combination creates new behavior without reopening their implementations.

021 — Big Ball of Mud — Brian Foote and Joseph Yoder

Recognize the economic and organizational forces that turn systems into sprawling structures of expedient patches, blurred boundaries, and shared state. Such systems persist because local fixes meet immediate needs, architectural knowledge is scarce, and wholesale reconstruction is risky. Preserve habitable regions through continuous refactoring, clear boundaries, and reconstruction in small areas, while treating decay as a pressure to manage rather than a moral failure.

022 — CAP Twelve Years Later: How the “Rules” Have Changed — Eric Brewer

CAP becomes more useful when you replace 'pick two' with decisions tied to particular operations, data, and moments in a partition. Consistency and availability are spectra, and a system may detect a partition, limit the affected work, trade one property temporarily, and recover explicitly afterward. Design the normal and partitioned modes together, including how state converges once communication returns.

023 — Life Beyond Distributed Transactions: An Apostate’s Opinion — Pat Helland

Build large distributed applications around independently transactional entities that exchange messages rather than around one global transaction. Once data crosses an entity boundary, accept uncertainty, duplicate delivery, delayed knowledge, and the need for idempotent or compensating behavior. Record durable histories and make business rules tolerate tentative outcomes, because coordination at scale resembles agreements among organizations more than updates inside one database.

024 — Building on Quicksand — Pat Helland and David Campbell

Stop treating globally shared, synchronously updated state as the natural foundation of scalable systems. Data observed across distance is inevitably delayed and may be contradictory, so build on stable identifiers, immutable versions, local transactions, and explicit rules for interpreting incomplete knowledge. Make uncertainty part of the data model rather than hiding it behind an interface that promises a single instantaneous truth.

025 — Immutability Changes Everything — Pat Helland

Cheap storage and computation make immutable facts a practical foundation for systems whose current views are derived from history. Append-only logs, snapshots, versioned datasets, copy-on-write structures, and idempotent computation reduce coordination and make retries, replication, and parallel processing safer. Separate semantic immutability from physical representation so you can reorganize data for efficient reads without changing what it means.

026 — The Log: What Every Software Engineer Should Know About Real-Time Data’s Unifying Abstraction — Jay Kreps

Model a stream of changes as an ordered, append-only log, then let consumers maintain their own materialized views by replaying it. The same abstraction explains database commit logs, replication, change capture, messaging, and stream processing, turning data integration into controlled propagation of state transitions. Preserve ordering and position so systems can recover, bootstrap new consumers, and reason about the relationship between historical facts and current state.

027 — Scalability! But at What COST? — Frank McSherry, Michael Isard, and Derek G. Murray

Demand a competent single-threaded baseline before celebrating a system's scale-out graph. Compute the configuration that first beats that baseline—the COST, or configuration that outperforms a single thread—because parallel frameworks can spend enormous resources recovering overhead they introduced themselves. Optimize useful work and elapsed time, not merely the ability to occupy more machines.

028 — The Tail at Scale — Jeffrey Dean and Luiz André Barroso

When a request fans out across many components, even a rare slow response becomes likely to delay the whole result. Reduce variability within services, then use cross-request techniques such as hedged requests, backup work, and latency-aware partitioning to keep outliers from dominating user experience. Apply redundancy selectively so tail improvement does not create uncontrolled extra load.

029 — Ironies of Automation — Lisanne Bainbridge

Expect automation to remove routine practice while leaving the human responsible for rare situations requiring the deepest skill. If operators only monitor a reliable system, their attention fades and their ability to diagnose unfamiliar failures decays precisely when intervention becomes necessary. Design automation to keep people informed, practiced, and able to form an accurate model of the process—not merely available as a last-resort component.

030 — An Investigation of the Therac-25 Accidents — Nancy G. Leveson and Clark S. Turner

Read the Therac-25 accidents as a systems failure spanning unsafe software, reused assumptions, weak interfaces, inadequate testing, poor incident communication, and misplaced confidence in software reliability. Do not infer safety from the absence of hardware interlocks or from the rarity of reproduced failures; specify hazards and enforce independent defenses. Treat operators' reports as evidence and share incident knowledge promptly, because safety depends on the organization surrounding the code as much as on the code itself.

031 — They Write the Right Stuff — Charles Fishman

Build reliability by making every change explicit, reviewed, and traceable. The Shuttle team controlled requirements, kept detailed records, used independent verification, and preferred proven code to unnecessary novelty. Measure the process and study every defect so dependable behavior follows from repeatable engineering rather than individual heroics.

032 — Monolith First — Martin Fowler

A well-structured monolith gives a new domain room to reveal its boundaries before distribution makes those boundaries expensive to change. Microservices amplify the cost of a mistaken split through remote communication, deployment coordination, and data ownership, while in-process modules are easier to move as understanding develops. Extract services only after experience reveals stable boundaries and the operational benefits outweigh distribution's premium.

033 — Is High Quality Software Worth the Cost? — Martin Fowler

Distinguish external quality, which users can observe, from internal quality, which determines how safely and quickly you can change the product. Cutting internal quality may appear faster briefly, but accumulated friction soon raises the cost of every feature and defect repair, reversing the apparent tradeoff. Keep the codebase healthy as an economic investment in future delivery speed, not as an aesthetic indulgence.

034 — Mocks Aren’t Stubs — Martin Fowler

Distinguish state-based tests that inspect results from behavior-based tests that set expectations on collaborators; mocks are not merely convenient stubs. A classical style uses real collaborators when practical and substitutes only awkward dependencies, while a mockist style isolates each object and specifies its outgoing interactions. Choose deliberately, because the testing style pushes design toward different collaboration patterns, coupling, and refactoring behavior.

035 — Microservices — James Lewis and Martin Fowler

Microservices are an organizational and operational architecture, not merely a collection of small processes. Build independently deployable services around business capabilities, give product teams responsibility for running them, and keep intelligence at the endpoints rather than in elaborate transport infrastructure. Accept the price explicitly: remote calls, distributed data, eventual consistency, and failure-aware automation demand capabilities that a monolith may not require.

036 — Test Pyramid — Martin Fowler

Shape your automated test suite as a pyramid: keep a broad base of fast, focused unit tests, add a thinner service-level layer, and reserve relatively few end-to-end UI tests for behavior that genuinely crosses the whole system. A top-heavy suite runs slowly, fails ambiguously, and becomes expensive to maintain. Use the proportions as a heuristic rather than a quota, optimizing for rapid feedback and clear diagnosis.

037 — Is TDD Dead? — Martin Fowler, Kent Beck, and David Heinemeier Hansson

Examine test-driven development as a collection of design and feedback practices rather than a ceremony that must be defended whole. Ask whether isolated tests, mocks, and a test-first rhythm improve your particular design, and notice when they instead couple tests to implementation or distort architecture. Keep the discipline of short feedback loops while remaining willing to change the technique when its costs exceed its signal.

038 — The Joel Test: 12 Steps to Better Code — Joel Spolsky

Twelve concrete questions can expose whether a software team has the basic engineering infrastructure needed to work well. Check source control, one-step builds, daily builds, defect tracking, schedules, specifications, working conditions, tools, testing, usability, and hiring. The score is deliberately crude, but fixing the gaps it reveals removes recurring friction before you reach for elaborate process reforms.

039 — Fire and Motion — Joel Spolsky

Preserve forward motion when the work feels uncertain, political, or dull: writing code, fixing defects, and shipping small improvements keeps a team’s initiative alive. Competitors and distractions can pin you down by consuming attention even when they do not produce anything better. Turn intimidating projects into the next concrete action and let consistent progress compound.

040 — Back to Basics — Joel Spolsky

Learn what string operations and memory access cost beneath the conveniences of a high-level language. A seemingly innocent abstraction can conceal repeated scans or copies, turning straightforward code into an accidental quadratic algorithm. Reason from data representation and operation count first, then choose abstractions with a clear view of the work they perform.

041 — The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) — Joel Spolsky

Text is not a bag of bytes whose meaning can be guessed later. Distinguish characters from code points and encoded byte sequences, understand what Unicode and UTF-8 each specify, and carry encoding metadata across every boundary. Decode input deliberately, work with a defined character model internally, and encode explicitly on output so text is not silently corrupted.

042 — What Color is Your Function? — Bob Nystrom

Notice how a language’s async model can split otherwise similar functions into incompatible “colors,” forcing the distinction through every caller in the stack. Colored functions are harder to compose because one kind cannot freely call the other, and the burden spreads across APIs rather than staying at the operation that waits. Prefer runtimes and abstractions that hide scheduling mechanics without hiding latency or failure.

043 — C Is Not a Low-level Language — David Chisnall

Abandon the idea that C is portable assembly for contemporary processors. Its abstract machine omits capabilities modern hardware needs to express—parallelism, rich memory behavior, and provenance among them—while aggressive compilers exploit undefined behavior in ways that break machine-level intuition. Choose a language and intermediate representation that can communicate your intended semantics to both the optimizer and the hardware.

044 — What Every Programmer Should Know About Memory — Ulrich Drepper

Modern processors often spend more time waiting for data than computing on it, which makes the memory hierarchy part of your design. Understand caches, cache lines, associativity, prefetching, write behavior, NUMA placement, and contention well enough to predict when access patterns will stall. Measure with hardware-aware tools, organize data for locality, and reduce needless movement before tuning arithmetic.

045 — The Lost Art of C Structure Packing — Eric S. Raymond

Read a C structure as a layout contract shaped by member sizes, alignment rules, and padding, not merely as a list of fields. Reordering members can sharply reduce memory consumption when millions of instances amplify a few wasted bytes. Verify the ABI and compiler rules you actually target, and use explicit packing only when its portability and access penalties are understood.

046 — What Every Computer Scientist Should Know About Floating-Point Arithmetic — David Goldberg

Treat floating-point numbers as finite approximations governed by precise rounding rules, not as slightly unreliable real numbers. Learn to reason in relative error and ulps, recognize cancellation and exceptional values, and understand the guarantees supplied by IEEE arithmetic. Reformulate unstable calculations and specify rounding behavior when numerical reproducibility or correctness matters.

047 — Execution in the Kingdom of Nouns — Steve Yegge

A simple action becomes difficult to follow when a design forces it through a ceremonial hierarchy of classes. Turning verbs into noun-heavy command objects, factories, and visitors scatters the operation across excessive structure. Give behavior first-class representation and introduce objects where identity, state, and substitution genuinely earn them.

048 — Falling Into The Pit of Success — Jeff Atwood

Make the easiest path through an API the one that produces correct, secure, and maintainable behavior. Documentation and discipline cannot compensate for interfaces whose defaults invite misuse or whose dangerous operations look ordinary. Move invariants into the design so callers succeed by following the obvious route and must work deliberately to escape it.

049 — Semantic Compression — Casey Muratori

Begin with direct code that expresses the cases you actually understand, even when that creates duplication. Watch repeated semantics emerge, then compress them into an abstraction that has a stable meaning rather than merely a similar textual shape. Delay generalization until the examples teach you the right boundary, because premature compression stores incorrect assumptions in a form that is harder to undo.

050 — Learnable Programming — Bret Victor

A programming environment can teach by making the program's behavior visible while the learner constructs it. Show values, control flow, time, and relationships; connect code to its output immediately; and let the learner manipulate concrete examples before requiring symbolic prediction. Simplified syntax and motivational games are not substitutes for a medium that supports a correct mental model of computation.

051 — Magic Ink: Information Software and the Graphical Interface — Bret Victor

Design information software first as a medium that helps a person understand and decide, not as a collection of controls. Infer context where possible, present relevant relationships clearly, and spend interaction only where the user must genuinely supply information or express intent. Treat graphic design as the organization of meaning, then add manipulation with the same care you would add complexity to a program.

052 — Responsive Web Design — Ethan Marcotte

Let a web layout respond to its viewing context instead of assuming a fixed canvas. Combine fluid grids, flexible media, and media queries so proportions and hierarchy survive across widths without maintaining a separate design for every device. Start from relationships among elements, test where those relationships break, and introduce breakpoints around the content rather than around fashionable screen sizes.

053 — A Dao of Web Design — John Allsopp

The web does not behave like a fixed printed page: readers bring different windows, fonts, devices, preferences, and constraints. Rigid pixel control turns that ordinary variability into failure. Specify enough structure to preserve meaning, then allow the browser and reader to negotiate the final presentation.

054 — Cool URIs don't change — Tim Berners-Lee

Design a URI to outlive the server, framework, file format, and organizational chart that first produced it. Put durable concepts—not implementation details—into public identifiers, and maintain redirects when infrastructure must change. Every broken address transfers your internal migration cost to readers, citations, indexes, and archives that cannot coordinate with you.

055 — The Web’s Grain — Frank Chimero

Work with the web’s native grain: a fluid vertical flow of text, links, and containers that can adapt across contexts. Begin from the smallest coherent expression, let layout grow from the content, and use frameworks or visual polish only after the relationships are sound. The browser is not an inferior page-composition tool; its flexibility is the material you are designing with.

056 — The Website Obesity Crisis — Maciej Ceglowski

Page weight and complexity are product decisions with human costs, not harmless implementation details that future bandwidth will erase. A simple document should not require megabytes of scripts, surveillance machinery, and fragile dependencies merely to place text on a screen. Set explicit performance budgets, remove work that does not serve the reader, and make the useful content arrive first.

057 — Web Design: The First 100 Years — Maciej Ceglowski

Design for a mature, constrained web rather than an endlessly accelerating technological future. Hardware, bandwidth, attention, and social tolerance encounter limits, while supposedly obsolete protocols and interfaces persist because they are useful and widely embedded. Build accessible, durable, participatory systems, and ask what human purpose a novelty serves before making the world adapt to it.

058 — The Internet With a Human Face — Maciej Ceglowski

Build online systems around human limits: people forget, change, make mistakes, and need private spaces that do not become permanent dossiers. Advertising-funded surveillance and centralized data collection turn ordinary behavior into durable institutional power while offering users little meaningful consent or recourse. Minimize retained data, preserve room for anonymity and forgetting, and make business incentives answer to the people living inside the system.

059 — Software Disenchantment — Nikita Prokopov

Faster hardware has not prevented software from becoming slower, larger, and less reliable. Layers of dependencies, background work, and indifferent craftsmanship consume memory, battery, bandwidth, and human attention without proportionate value. Measure responsiveness from the user's machine, reduce the stack you ask them to carry, and treat efficiency as part of correctness.

060 — Falsehoods Programmers Believe About Names — Patrick McKenzie

Model names as user-provided, culturally contingent data rather than as a universal first-name/last-name tuple. People may have one name, many names, changing names, punctuation, unfamiliar scripts, or forms that exceed your chosen length and ordering rules. Store what the domain truly requires, preserve the user’s representation, and avoid validation that encodes your local custom as a law of identity.

061 — Falsehoods Programmers Believe About Time — Noah Sussman

Clocks jump, drift, disagree, repeat values, and obey political rules rather than tidy arithmetic. Durations, civil timestamps, time zones, and monotonic ordering are different concepts and should not share an accidental representation. Use monotonic clocks for elapsed time, carry zone context for human schedules, and test the discontinuities your ordinary day conceals.

062 — Programming Sucks — Peter Welch

Accept that production software is assembled atop incomplete knowledge, mutable requirements, leaky abstractions, and systems no one fully understands. Small changes can awaken interactions far beyond their apparent scope, while success often means maintaining a fragile truce among historical constraints. Keep designs comprehensible, investigate failures without pretending the system is simple, and leave the next programmer enough context to continue.

063 — Local-First Software: You Own Your Data, in Spite of the Cloud — Martin Kleppmann, Adam Wiggins, Peter van Hardenberg, and Mark McGranaghan

Put a user’s primary copy of data on their own device and make the network an aid to collaboration rather than a gate to basic work. Preserve offline availability, fast local interaction, multi-device synchronization, long-term access, privacy, and user control while using conflict-free replicated data types where concurrent edits must merge. Judge cloud architecture by whether people retain agency when the service is slow, discontinued, or unwilling to cooperate.

064 — Hyrum’s Law — Hyrum Wright

At sufficient scale, every observable API behavior becomes somebody's dependency, whether or not you documented or intended it. Performance, error text, ordering, timing, and quirks can harden into de facto contracts. Limit what consumers can observe, measure real usage before changing behavior, and plan migrations as compatibility work rather than assuming the specification defines the whole interface.

065 — Cargo Cult Software Engineering — Steve McConnell

Select engineering practices for the mechanisms and evidence that make them effective, not for their resemblance to what successful organizations do. Process becomes cargo cult when a team copies visible rituals while ignoring context, feedback, and the causal work underneath them. Form hypotheses about quality and productivity, measure the outcome, and revise the practice when reality disagrees.

066 — Software Quality at Top Speed — Steve McConnell

Stop framing quality and delivery speed as automatic opposites. Defects create rework, destabilize schedules, and grow more expensive as they travel downstream, so disciplined prevention and early removal usually shorten the path to release. Invest in practices that expose mistakes close to their source, then use defect and rework data to find the quality level that supports sustained throughput.

067 — On Being a Senior Engineer — John Allspaw

Seniority shows up in judgment under uncertainty, not in tenure or the volume of code produced. Build shared context, explain tradeoffs, ask questions that expose hidden assumptions, and help the team recover when systems and plans fail. Exercise influence without needing authority, keep technical depth connected to business consequences, and make the people around you more capable.

068 — Being Glue — Tanya Reilly

Notice the coordination, onboarding, review, and gap-filling work that makes a technical team effective even though it produces little visible code. Choose that work deliberately: make its impact legible, share genuinely non-promotable tasks fairly, and keep enough of your evaluated craft in your workload to grow toward the career you actually want. If you manage others, do not let glowing feedback conceal a widening gap between what someone contributes and what your promotion system rewards.

069 — Staff Engineer: Archetypes — Will Larson

Staff engineering is not one uniform job. You may guide a team as a Tech Lead, steward a durable technical domain as an Architect, resolve high-stakes ambiguity as a Solver, or extend an executive's reach as a Right Hand. Identify which form of leadership the organization actually needs, then choose work that fits both that need and the way you create leverage.

070 — The Engineer/Manager Pendulum — Charity Majors

Treat management as a change of profession, not a promotion or a permanent departure from engineering. Move between the two tracks when the work calls you: management teaches conflict, motivation, and business context, while hands-on engineering renews the technical judgment and credibility that decay away from the code. Do each role fully rather than attempting both at once, since people and difficult technical problems each require sustained attention.

071 — Maker's Schedule, Manager's Schedule — Paul Graham

Protect creative work in half-day blocks, because an hour is often only enough time to load a difficult problem into your head. A meeting that looks like one cheap calendar slot to a manager can split a maker's day into unusable fragments and suppress ambitious work before it begins. Cluster appointments into office hours or the edges of the day so coordination remains possible without repeatedly throwing exceptions through someone's concentration.

072 — Don't Call Yourself A Programmer, And Other Career Advice — Patrick McKenzie

Employers buy reduced costs, increased revenue, and managed risk—not programming as an identity. Frame your work around those business outcomes, and learn how companies hire, budget, negotiate, and value line-of-business software even when the technical work looks ordinary from inside the profession. Build communication and commercial judgment alongside coding skill so other people can recognize the leverage you already produce.

073 — Salary Negotiation: Make More Money, Be More Valued — Patrick McKenzie

Negotiate compensation as a consequential business conversation, not as a referendum on your worth or gratitude. Preserve information and optionality: avoid naming the first number, keep multiple opportunities alive, and evaluate the complete offer only after the employer has decided it wants you. Ask calmly for an improvement backed by the value and alternatives you bring; a short uncomfortable exchange can outweigh years of routine raises.

074 — Beating the Averages — Paul Graham

Choose technology for the advantage it gives the product, even when conventional employers would reject it as unusual. A more expressive language can compress implementation, expose better abstractions, and let a small team test ideas faster, while competitors may not recognize the advantage because their own tools constrain what they can imagine. Keep the strategic language choice close to the people who understand both the code and the business rather than reducing it to a standardized hiring decision.

075 — Hackers and Painters — Paul Graham

Programming often behaves more like a maker's craft than a process of executing a complete specification. Begin with a working sketch, learn from the artifact, and revise toward a form you could not fully predict in advance. Let language, tools, and taste expand the designs you can think, while preserving enough autonomy and contact with users for iterative discovery to happen.

076 — Taste for Makers — Paul Graham

Train your taste by studying why your earlier choices were worse, not by declaring every preference equally good. Seek designs that are simple, durable, fitted to the real problem, open to recombination, and easy to revise; apparent effortlessness usually arrives only after exacting iteration. Pay attention when expertise makes a tolerated convention look ugly, because that discomfort can identify work worth improving.

077 — How to Get Startup Ideas — Paul Graham

Look for problems you know firsthand instead of brainstorming products that merely sound like startups. Start with a small group that urgently needs a solution you can build, choosing a narrow, deep well of demand over a broad audience that feels mild hypothetical interest. Put yourself near a changing frontier, notice what is newly missing, and let a real need pull the company into existence.

078 — Do Things that Don't Scale — Paul Graham

Early-stage work often has to be done by hand before you know what deserves to scale. Recruit users manually, give them an unexpectedly attentive experience, and perform whatever the immature product cannot yet automate. That effort teaches you what users need, creates the first momentum for growth, and reveals which processes deserve machinery later.

079 — Startup = Growth — Paul Graham

Define a startup by its pursuit of rapid growth, not by age, size, funding, or the use of fashionable technology. Choose a large reachable market, measure a meaningful growth rate every week, and let that constraint force decisions about product, hiring, and financing. Once growth becomes the compass, many debates become concrete: choose the action most likely to move the rate without destroying the foundations that sustain it.

080 — Schlep Blindness — Paul Graham

Examine ideas you reflexively avoid because they involve sales, operations, regulation, payments, or other tedious work. That reluctance is shared by other capable builders, so the unpleasant execution may be the barrier protecting a valuable opportunity rather than evidence that the idea is bad. Separate a truly intractable business from a merely unappealing schlep, then decide whether the avoided work is precisely where you can create leverage.

081 — Keep Your Identity Small — Paul Graham

The more tightly a belief is attached to your identity, the harder it becomes to examine honestly. Disagreement then feels like an attack, and inquiry becomes socially dangerous for everyone involved. Keep your identity small and your ideas available for revision so you can follow an argument past tribal boundaries without having to defend yourself along with the claim.

082 — The Top Idea in Your Mind — Paul Graham

Guard the problem that occupies your mind when your attention is free, because background thought continues working on it long after you leave the desk. Money disputes, status anxiety, and other unresolved concerns can silently displace the question you most want your best thinking to advance. Audit what appears in quiet moments and change your circumstances when the wrong concern has become cognitively expensive.

083 — How to Do Great Work — Paul Graham

Great work usually starts with curiosity strong enough to carry you to a field's frontier. Look for a problem that is important, tractable, and genuinely interesting to you; ambition supplies direction, but sustained interest provides the energy for years of learning and revision. Produce often, seek excellent peers, preserve morale through setbacks, and let discoveries redirect the plan instead of demanding a straight path from the beginning.

084 — Good and Bad Procrastination — Paul Graham

Distinguish avoiding all work from postponing a smaller obligation because a harder, more important problem has taken hold of you. Significant work often requires long, apparently unproductive stretches and a willingness to disappoint the endless errands that present themselves as urgent. Ask what you are protecting by procrastinating, then arrange your life so the answer can be a consequential project rather than mere escape.

085 — Great Hackers — Paul Graham

Create conditions in which exceptional programmers can spend their ability on the product instead of fighting weak tools, office politics, and arbitrary process. Give them hard problems, strong colleagues, autonomy over implementation, and a language expressive enough to keep ideas close to code. Do not confuse credentials, age, or conformity with ability; judge hackers by what they can make and by the clarity of the thinking embedded in it.

086 — How to Work Hard — Paul Graham

Hard work lasts when genuine interest, a worthy aim, and an honest sense of your best effort reinforce one another. Push toward the edge of your capacity while protecting health and the curiosity that makes sustained effort possible, then calibrate yourself against people doing excellent work. Discipline matters most when it keeps you returning to the problem through uncertainty, not when it merely lengthens the visible workday.

087 — How to Be Polite — Paul Ford

Practice politeness as an operational discipline: answer people, arrive when you said you would, remember names, apologize cleanly, and make ordinary interactions easier than they might have been. Assume that other people carry contexts you cannot see, and leave them room to recover from awkwardness or error without turning every lapse into a verdict. Courtesy will not resolve every conflict, but it reduces needless friction and keeps repeated relationships workable.

088 — Ten Lessons I Wish I Had Been Taught — Gian-Carlo Rota

Give people something concrete to take home: organize a lecture around one memorable point, write for readers rather than prestige, and use clear exposition to discover whether you understand your own subject. Protect time for sustained work, learn the useful tricks of your profession without mistaking them for depth, and treat reputation as the accumulation of what others can reliably associate with you. Choose which obligations to neglect consciously, because a serious intellectual life cannot optimize everything at once.

089 — Solitude and Leadership — William Deresiewicz

Leadership requires enough distance from messages, institutional incentives, and other people's finished opinions to form a thought of your own. It means more than executing assigned goals well; you must decide which goals matter, defend a new direction, and resist the conformity that bureaucracies reward. Develop that independence through concentration, reflective conversation, and solitary work that lets your first conventional answer mature into judgment.

090 — The Bitter Lesson — Rich Sutton

Design AI methods to benefit from increasing computation rather than encoding ever more of your own domain knowledge. Across games, speech, and vision, handcrafted insight produced attractive short-term gains but eventually plateaued behind general search and learning methods that scaled. Build mechanisms that can discover useful structure for themselves, because the world's complexity is too open-ended to be captured as a fixed inventory of human-designed features.

091 — Why Software Is Eating the World — Marc Andreessen

Treat software as the operating core of industries that once regarded it as a support function. Falling computing costs, broad connectivity, and a large population of capable developers let software-native companies reach customers and reorganize markets from books and music to logistics, finance, and energy. Evaluate incumbents by whether they can become software organizations quickly enough, not merely by whether they have purchased modern systems.

092 — A little bit of plain JavaScript can do a lot — Julia Evans

The browser's built-in APIs can carry a small application farther than framework-first habits suggest. Event listeners, DOM methods, URL state, forms, and a little CSS support useful interactive tools with code that remains easy to inspect and change. Add dependencies when they solve a demonstrated problem, not before you have tested how far the platform itself can carry the design.

093 — Choices — Joel Spolsky

Remove decisions that do not help the user accomplish the task. Every option forces someone to understand a distinction, predict a consequence, and accept responsibility for choosing, so configurability carries a cognitive cost even when implementation is cheap. Select strong defaults, infer what the program can know, and reserve explicit choices for differences that users genuinely understand and care about.

094 — Application compatibility layers are there for the customer, not for the program — Raymond Chen

Regard a compatibility workaround as protection for the person who owns broken software, not as a new API contract for that software to exploit. The platform may detect an old program and emulate earlier behavior so a customer can keep working, but depending on the shim converts a temporary rescue into deliberate fragility. Program to documented behavior and let compatibility layers remain an implementation detail that can disappear when the original need does.

095 — John Carmack on Inlined Code — John Carmack

Code can be easier to reason about when its execution and state changes read locally from top to bottom. Splitting that story into one-use helpers may scatter mutable dependencies across a file, although function boundaries remain valuable when they create strong contracts. Move separable logic toward pure functions and single-assignment values; purity improves reasoning even when the surrounding system cannot be purely functional.

096 — Rob Pike's 5 Rules of Programming — Rob Pike

Measure before optimizing, because the expensive path is rarely where intuition first places it. Keep algorithms and data structures simple until evidence shows that scale demands something clever, then measure again rather than trusting the elegance of the optimization. Invest most heavily in representing and organizing the data well: once the structure is right, the necessary algorithms often become obvious.

097 — Design Principles Behind Smalltalk — Daniel H. H. Ingalls

Build a computing environment from a small set of concepts applied consistently: everything is an object, computation happens through messages, and each component owns the interpretation of its state. Preserve a live, inspectable system in which users can understand and change the tools they use rather than being separated from them by fixed applications. Favor a simple, uniform kernel that can describe itself and grow through composition over a large language full of special cases.

098 — The Error Model — Joe Duffy

Recoverable environmental failures and bugs that violate program invariants need different treatment. Make expected errors visible in types and signatures so callers must confront them, while failing fast on corrupted state instead of pretending execution can safely continue. Design the language, runtime, contracts, and diagnostics together: error handling shapes control flow and reliability too deeply to be repaired by adding exceptions after the rest of the system is settled.

099 — Structured Programming with go to Statements — Donald E. Knuth

Treat structured programming as a method for making control flow provable and comprehensible, not as a lexical ban on goto. Choose constructs according to the reasoning they permit and the performance the problem actually needs; some well-disciplined jumps express exits, state machines, or optimized inner loops more clearly than contorted alternatives. Replace slogans with measured analysis, preserving structure while allowing carefully justified exceptions.

100 — The Early History of Smalltalk — Alan C. Kay

Follow Smalltalk's development as an iterative search for a personal dynamic medium, not merely the invention of an object-oriented language. Objects, messages, late binding, graphical interfaces, networks, and children's learning experiments evolved together because the system was repeatedly rebuilt around a compact generative idea. Design ambitious systems by protecting the principles that let them grow, while remaining willing to replace implementations that no longer serve the larger vision.

· 5 min read

Good Reads

2026-06-17 You got faster; your company didn't { terriblesoftware.org }

image-20260617154820

Individual speedups from AI don't turn into faster shipping because the real bottlenecks — review, coordination, decisions — sit outside your editor. Local optimization runs straight into the system's actual constraint.

2026-06-21 How software groups rot: legacy of the expert beginner { daedtech.com }

image-20260621153243

Erik Dietrich's classic on why teams stagnate. An “expert beginner” stops improving early, mistakes tenure for mastery, and entrenches — and because they set the local standards, the whole group calcifies around their ceiling.

2026-07-01 Why I stopped arguing with people { wangcong.org }

image-20260701080407

On the cost of winning arguments that change no one's mind. The author's shift from debating to disengaging is framed as reclaimed attention rather than defeat — pick the arguments that actually move something.

2026-07-02 How to ask for help { pradyuprasad.com }

image-20260702081819

A guide to asking questions that actually get answered: show what you tried, state the goal, and make it cheap for someone to help you. Better questions get better and faster help — and respect people's time.

2026-07-07 98% isn't very much { whynothugo.nl }

image-20260707102655

Reliability is multiplicative. A step that works 98% of the time sounds excellent until you chain twenty of them and the whole pipeline fails more often than it succeeds. A short, sharp argument about why “nearly always” rarely is.

Emacs

2026-06-15 Even more batteries included with Emacs { karthinks.com }

image-20260615055002

A tour of capable built-in Emacs features people reach for packages to replace — completion, project handling, window management — that already ship in the box. The theme: learn what's included before installing over it.

2026-07-04 Magit 4.6 released { emacsair.me }

image-20260704201621

Release notes for Magit 4.6, the Git porcelain for Emacs. Worth skimming for the new commands and workflow changes if you drive Git from inside the editor.

🦶🔫 C || C++

2026-05-29 Let's compile Quake like it's 1997 { fabiensanglard.net }

image-20260529090313

Rebuilds the original Quake with a period-accurate 1997 DOS toolchain — Watcom C, DOS4GW — to show what shipping a game looked like before modern build systems. The interesting part is the friction: fixed memory models, segmented pointers, and a compiler whose optimizer you had to hand-hold.

2026-06-04 Intrusive data structures { tivrfoa.github.io }

image-20260604222512

The node's link fields live inside the element itself instead of in a separate container node — the Linux kernel list_head style. One fewer allocation per insert, better cache locality, and an element can belong to several lists at once. The tradeoff is that the structure and its storage are now coupled.

2026-06-04 sysprog21/intrusive-ds: intrusive data structures for C { github.com }

image-20260604222716

A small C library collecting intrusive lists and trees ready to drop into a project, companion code to the intrusive-structures write-up. Useful as a reference for the container_of trick and how the macros hide the pointer arithmetic.

2026-06-04 Branchless quicksort { tiki.li }

image-20260604234608

Removes the unpredictable branch from quicksort's partition step. Instead of an if that the CPU keeps mispredicting on random data, it computes both outcomes and selects with a conditional move, trading a few extra instructions for a big drop in branch-misprediction stalls.

2026-06-10 Klondike solitaire for curses in 5k of C { nanochess.org }

image-20260610235531

A full playable Klondike solitaire for the terminal in about 5 KB of C, from Óscar Toledo, who is known for extremely compact programs. Worth reading as an exercise in how much game fits into very little code when you drop every abstraction.

2026-06-19 Data Compression Explained { mattmahoney.net }

image-20260619231610

Matt Mahoney's book-length reference on lossless compression, from information theory and entropy through arithmetic coding, context mixing, and the PAQ family that has topped compression benchmarks. Dense but self-contained; a good single source for how modern compressors actually model data.

2026-06-22 microcrad: micrograd re-implemented in C { github.com }

image-20260622064657

Karpathy's micrograd — a tiny scalar autograd engine and neural net — rewritten in C. Small enough to read end to end, which makes backpropagation concrete: every value is a node in a graph, and gradients flow backward through it.

2026-06-29 Memory-safe context switching { fil-c.org }

image-20260629220543

How Fil-C — a memory-safe implementation of C and C++ — handles context switching without breaking its safety guarantees. Coroutine and thread switches move the stack out from under running code, exactly the kind of operation a naive safety scheme forbids; this explains how it stays sound.

2026-07-01 Text editor data structures: the piece table { averylaird.com }

image-20260701183520

Why serious editors don't store text as one big array. Compares gap buffers, ropes, and the piece table — the append-only original plus a list of pieces pointing into original and added buffers — which gives cheap edits and near-free undo. The structure VS Code settled on, and why.

2026-07-01 Engineering high-performance parsers with data-oriented design { arshad.fyi }

image-20260701183536

Applies data-oriented design to parsing: lay tokens out as struct-of-arrays, keep hot fields packed for the cache, and shape the loops so the branch predictor and prefetcher can keep up. The point is that parser speed is mostly a memory-layout problem, not a clever-algorithm problem.

2026-07-08 GTK's in-tree timsort in C { github.com }

image-20260708234759

GTK carries its own C implementation of timsort — the adaptive merge sort that exploits existing runs of ordered data. Reading production timsort shows the parts the textbook description skips: run detection, galloping merges, and the delicate invariant that bounds the merge stack.

· 8 min read

[[TOC]]

Good reads

2026-05-25 Using AI to write better code more slowly { nolanlawson.com }

image-20260525221418

A counter to the idea that AI coding means shipping low-quality code fast. Lawson argues LLMs are just as effective at writing high-quality code slowly: use them to find bugs — throw enough passes at a codebase and they surface plenty — then spend your effort prioritizing and validating rather than typing. His trick is running several different models over the same change so hallucinated or bogus findings cancel out.

2026-05-31 Domain Expertise Has Always Been the Real Moat | Aaron Brethorst { brethorsting.com }

image-20260531092212

The hard part of software was never typing the code — it was building an accurate model of the domain in your head first. Before shipping a payroll system you had to understand garnishments and pre-tax deductions; before a transit app, what a GTFS feed is and why a bus that's “on time” can still be wrong. The code was a transcription of that understanding, and that understanding is what AI doesn't hand you for free.

2026-06-13 If You are Asking for Human Attention, Demonstrate Human Effort | Tom Bedor's Blog { tombedor.dev }

image-20260613231834

As more debugging, writing, and code comes from AI, a new etiquette question appears: when is it OK to forward AI output to another human to read? The argument — if you're asking for someone's attention, show that you put in human effort first.

2026-06-15 Perlisisms - "Epigrams in Programming" by Alan J. Perlis { cs.yale.edu }

image-20260615055251

Alan Perlis's “Epigrams in Programming” — 130-odd sharp, quotable one-liners (“A language that doesn't affect the way you think about programming is not worth knowing”). Durable aphorisms worth rereading every so often.

Dev

2026-05-24 Implementing the Inbox Pattern for Reliable Message Consumption { milanjovanovic.tech }

image-20260524145208

The Outbox pattern guarantees reliable publishing; the Inbox pattern covers the consumer side, ensuring each incoming message is processed exactly once even when the broker retries or delivers duplicates. Walks through an implementation in .NET with MassTransit and PostgreSQL: the inbox schema, the consumer, and the background processor.

Retro Historical

2026-07-10 The tech of Terminator 2: an oral history { vfxblog.com }

image-20260710204700

image-20260710204701

The people who built the effects for Terminator 2 tell the story in their own words — ILM artists, Cameron's team, and the crew behind the T-1000. The film had roughly 50 effects shots, a number Eric Enderton contrasts with today: “today you can't get out of bed for less than 300 shots.” The liquid-metal morphs, the chrome man walking out of fire, the digital body doubles — all done when the tools barely existed and much of the pipeline was invented on the show. A first-hand record of the moment computer graphics crossed into believable, photoreal character work.

2026-05-24 A History of IDEs at Google { laurent.le-brun.eu }

image-20260524144702

How Google's internal developer tooling evolved from a fragmented set of editors toward a shared, browser-based Cloud IDE. A firsthand look at what editing code at that scale demands and why the ecosystem kept splintering before it consolidated.

C

2026-06-28 nnevskij/wordle.c: Wordle game in less than 200 LOC written in C { github.com }

image-20260628175427

Wordle implemented in under 200 lines of C — small enough to read in one sitting, with a few advanced options. A tidy example of how little code the game actually needs.

2026-06-28 Ported my game built in C to WASM, here's every bug I hit : r/C_Programming { reddit.com }

image-20260628175947

A blunt postmortem of porting a hand-written C game engine (bgfx, SDL2, miniaudio, cimgui) to the web via Emscripten. A numbered list of every non-obvious problem hit along the way — starting with being forced back to Visual Studio because the author's debugger doesn't support 32-bit processes. Practical, pain-saving notes for anyone shipping C to WASM.

2026-05-23 tspader/sp: A modern C standard library { github.com }

image-20260523112747

A modern C standard library delivered as a single header, sp.h, bundling the utilities C leaves out. The examples go as far as a small ls implementation to show the library in real use.

2026-05-23 antirez/sds: Simple Dynamic Strings library for C { github.com }

image-20260523153652

Simple Dynamic Strings: antirez's small, widely-copied C string library (the one from Redis). Its trick is storing the length in a header that sits before the character pointer, so SDS strings stay compatible with plain C string functions while gaining O(1) length and safe growth. The README lays out the design and its tradeoffs.

2026-05-25 On C extensions, portability, and alternative compilers { lemon.rip }

image-20260525082242

A working list of the non-standard behaviors and compiler/library gaps you hit writing “portable” C, collected while building a C compiler. Concrete friction points across glibc, SDL, OpenBSD libc, and bionic show how rarely real-world code is actually pure ISO C.

2026-07-06 The LLVM Compiler Infrastructure – Communications of the ACM { cacm.acm.org }

image-20260706180115

A retrospective on LLVM by its creators, Vikram Adve and Chris Lattner, tracing the project from NSF-funded research in 2000 to code that now runs on billions of devices. Covers its major innovations, its external impact, and where the infrastructure is still heading — and doubles as a case for federal funding of basic research.

2026-07-09 ironrinox/mini-c-compiler: Minimal C compiler implemented in C for educational purposes { github.com }

image-20260709215409

A minimal C compiler written in C for learning — lexer, parser, AST, and interpreter — small enough to read end to end and understand how each stage feeds the next.

Hey, Zig!

2026-06-08 boringcollege/zig-by-example: Zig by example { github.com }

image-20260608083323

A hands-on introduction to Zig through annotated examples, in the spirit of Go by Example. It leans on the language's focus on robustness and simplicity — no hidden control flow, no hidden allocations, no preprocessor. Readable online or straight from the repo.

Bookmarklets

2026-06-01 Bookmarklets Collection { tools.simonwillison.net }

image-20260601080342

A collection of small, useful bookmarklets for web development and browsing, with drag-to-install buttons and a copy option for mobile. Includes handy ones like viewing a page's reference anchors or its source.

But Better

2026-05-26 laminar-run/beautiful-mermaid: Simple React App for beautiful Mermaid Diagrams { github.com }

image-20260526094037

A small React app that renders Mermaid diagrams with far nicer styling than the defaults — paste diagram source and get a clean, presentable result ready to drop into a doc or slide.

Good Prompts

2026-05-24 obra/superpowers: An agentic skills framework & software development methodology that works { github.com }

image-20260524232235

An agentic “skills” framework and development methodology for Claude: a structured library of reusable skills plus a workflow for applying them. Includes a quickstart and an explanation of how the skills compose into a repeatable process.

More RSS

2026-06-02 RSS Is Back. AI Agents Are Reading It. — Julien Reszka { julienreszka.com }

image-20260602180157

RSS never actually died — it quietly powered podcasting for a decade — and now AI agents need exactly what it provides: a clean, structured, pollable feed of content. A short argument that the old format is unexpectedly well-suited to the agent era.

2026-06-02 prof18/feed-flow: FeedFlow is a minimalistic RSS Reader available on Android, iOS, macOS, Windows and Linux { github.com }

image-20260602231420

FeedFlow is a minimalist cross-platform RSS reader (Android, iOS, macOS, Windows, Linux) built with Kotlin Multiplatform, Jetpack Compose, and SwiftUI. A clean, single-codebase example of a modern KMP app as much as a usable reader.

2026-06-29 RSS Subscription Extension (by Google) - Chrome Web Store { chromewebstore.google.com }

image-20260629225452

Google's official RSS Subscription Extension for Chrome: adds one-click feed subscription to the toolbar and previews feeds in the browser. Rated 4.0★ across ~3.5K reviews.

2026-06-29 6 Best Free RSS Feed Readers For 2026 { bloggingwizard.com }

image-20260629225608

A roundup of six free RSS readers for 2026 — Feedly, Inoreader, NewsBlur, and others — compared on what each does well. A reasonable starting point if you're picking a reader.

2026-06-29 Feedbro - RSS Feed Reader with built-in Rule Engine { nodetics.com }

image-20260629225718

Feedbro is a browser-extension RSS/Atom/RDF reader (Chrome, Edge, Brave, Vivaldi, Firefox) with a built-in rule engine for filtering and auto-tagging feeds — handy for following many sources without drowning in them.

2026-06-29 lqdev/rss-browser-extension: RSS Feed Detector Extension { github.com }

image-20260629225747

A small browser extension that detects RSS/Atom feeds on the page you're viewing so you can subscribe quickly. Installable from source in developer mode.

· 12 min read

Developer Tools

2026-01-02 FracturedJson { github.com }

image-20260102092617004

FracturedJson formats JSON for human scanning without wasting vertical space. Small arrays and objects can stay on one line, repeated structures can align like a table, and larger data still breaks into readable blocks.

It is a good reminder that pretty-printing is an interface design problem. The best layout depends on shape, repetition, and the task a human is doing with the data.

2026-01-04 awesome-bookmarklets { github.com }

image-20260104145547011

A bookmarklet is still one of the lowest-friction ways to carry a tiny browser tool around. This collection keeps the idea visible: page inspection, DOM manipulation, quick formatting, and small utilities can live in the bookmarks bar without an extension build pipeline.

Several bookmarklet lists were saved in the export; this one is kept as the representative developer-focused collection.

2026-01-05 ticket: git-native issue tracking in one shell script { github.com }

image-20260105201611022

ticket stores project work items in git and runs as a single Bash script. The appeal is not enterprise workflow coverage; it is keeping issues, dependencies, priorities, and history close to the repository with almost no service dependency.

This kind of tool is useful for small teams, solo projects, and environments where the source repository is the most durable coordination surface.

2026-01-05 git-bug { github.com }

image-20260105201654023

git-bug turns bug tracking into data that travels with the repository. Issues can be created and edited offline, synchronized through git remotes, and bridged to external trackers when needed.

The long-lived idea is distributed project state: not every coordination artifact has to live in a central web app.

2026-01-09 opencode { github.com }

image-20260109090426033

opencode is an open-source coding agent with enough activity and surface area to be worth saving as a reference point. Even if the agent landscape changes quickly, the repository shows what users expect from a local coding agent: sessions, tools, model plumbing, editing, and review loops.

The project is time-sensitive, but the design pressure is durable: coding agents are becoming developer tools, not just chat boxes.

2026-01-13 superdiff { github.com }

image-20260113190102036

superdiff focuses on making differences readable across structured data, text, coordinates, streams, and files. It is useful when a normal line diff hides the semantic shape of the change.

Good diffs reduce review cost. A diff tool that understands structure can make data changes inspectable without requiring a custom viewer for every format.

2026-02-05 CG/SQL { ricomariani.github.io }

image-20260205080428043

CG/SQL lets developers write stored procedures in a T-SQL-like language and compile them into C or Lua code that uses SQLite's C API. It also handles schema evolution and test generation.

The interesting part is the boundary: SQLite stays embedded and portable, while complex database logic gets a higher-level authoring model and generated low-level code.

2026-04-03 Podroid: rootless Linux containers on Android { github.com }

image-20260403193849066

Podroid packages an Alpine-based Linux environment into a rootless Android app, with support for containers and GUI desktop applications. It turns a phone or tablet into a surprisingly serious Linux playground.

The project is worth saving as a reference for mobile development environments, Android virtualization boundaries, and the continuing pull of Unix tools onto every device people carry.

🔫 C || C++ / Systems

2026-01-06 Why SQLite is coded in C { sqlite.org }

image-20260106080434024

SQLite's argument for C is concrete: performance, compatibility, low dependency load, and long-term stability. A small embedded database library has to run almost everywhere and remain callable from almost everything.

The page is also a useful antidote to language monoculture. Safety and abstraction matter, but SQLite's constraints include ABI durability, toolchain reach, and decades of integration surface.

2026-01-16 From bare metal to containers { buildsoftwaresystems.com }

image-20260116080431037

This guide compares physical machines, virtual machines, containers, process sandboxes, and language virtual environments as points on the same isolation spectrum.

The useful framing is that containers are not magic portability boxes. They are one layer in a stack of kernel features, filesystem assumptions, process boundaries, and operational tradeoffs.

2026-01-17 The Arena: custom memory allocators in C { bytesbeneath.com }

image-20260117011043038

Arena allocation groups many allocations under one lifetime and frees them all at once. That can remove bookkeeping, reduce fragmentation, and make ownership easier to reason about when the program's phases are clear.

The tradeoff is discipline: arenas are powerful when lifetimes are simple and dangerous when long-lived references leak across phase boundaries.

2026-02-09 What functional programmers get wrong about systems { iankduncan.com }

image-20260209212523046

Type systems verify properties of programs, but production correctness belongs to the whole deployed system. Rolling deploys, old messages, multiple live versions, queues, migrations, and operational recovery all sit outside the neat boundary of a single build.

The durable lesson is that local reasoning is necessary but not sufficient. The unit that fails in production is often a fleet, a protocol, a migration path, or a historical message format.

2026-02-19 -fbounds-safety: enforcing bounds safety for C { clang.llvm.org }

image-20260219080811051

Clang's -fbounds-safety adds bounds annotations and checked pointer types to C while preserving interoperability with existing C code. The model is incremental: safer pointer defaults and annotations where the compiler needs more information.

This is worth saving because it shows one plausible path for improving C safety without pretending the C ecosystem can be rewritten all at once.

2026-02-21 canvas_ity: a single-header C++ 2D rasterizer { github.com }

image-20260221215143052

canvas_ity is a tiny single-header C++ rasterizer modeled on the basic HTML5 2D canvas API. It prioritizes portability and immediate-mode drawing over a large rendering stack.

Small graphics libraries are useful references because the whole system can fit in one reader's head: paths, transforms, fills, strokes, rasterization, and demos without a framework.

2026-03-30 Comprehensive C++ hashmap benchmarks { martin.ankerl.com }

image-20260330073156064

The benchmark compares C++ hash map implementations across insert, erase, lookup, iteration, memory, and reference-stability scenarios. The valuable part is the benchmark shape as much as the ranking.

Hash table performance is workload-specific. This is best read as a map of tradeoffs and measurement pitfalls, not as a permanent answer to which container is fastest.

2026-05-17 C++26 shipped a SIMD library nobody asked for { lucisqr.substack.com }

image-20260517082228069

The critique argues that std::simd misses many patterns real performance code needs, especially when width choices, compile times, and expressiveness matter. The practical recommendation is still to use intrinsics for hard parts and let the auto-vectorizer handle the easy parts.

Whether or not every conclusion holds for every workload, the post is useful because it names the abstraction mismatch: standard-library portability can become too narrow for the code that actually needs SIMD.

Web / Browsers / Frontend

2026-01-01 Web browsers have stopped blocking pop-ups { smokingonabike.com }

image-20260101014056000

Pop-up blocking used to be a visible browser victory over abusive advertising. The modern loophole is user activation: once a click or tap occurs, pages can often open windows again in flows that feel legitimate to the browser but hostile to the user.

The lesson is broader than pop-ups. Browser protections are only as strong as their interaction model, and attackers adapt to whatever action counts as consent.

2026-01-02 WASM-ImageMagick { github.com }

image-20260102205624006

WASM-ImageMagick brings ImageMagick into the browser through WebAssembly. It is useful both as a tool and as a concrete example of a large native library exposed to web code.

The project is a good reference for the rough edges of serious WebAssembly ports: compiled dependencies, sample UIs, API wrappers, and the difference between a demo and a usable browser-side utility.

2026-01-05 Shaders 103: smoke { garden.bradwoods.io }

image-20260105114652017

This shader note builds a smoke effect in three.js from texture sampling, UV mapping, masks, remapping, edge work, twist, and animation. The value is in the staged construction rather than a final copy-paste fragment.

Visual shader tutorials are easiest to remember when the screenshot carries the effect and the text explains the mechanism.

2026-01-09 What happened to WebAssembly { emnudge.dev }

image-20260109082125032

WebAssembly was sold as a web revolution, but its durable value is more specific: portable sandboxed execution, compilation targets, embeddability, and performance-sensitive modules in systems that can tolerate its boundaries.

The piece is useful because it separates hype from deployment reality. WebAssembly did not become a replacement for JavaScript applications, but it did become infrastructure in places where a safe portable binary target matters.

2026-01-13 Text-based web browsers and modern HTML { cssence.com }

image-20260113104117035

Modern HTML features such as details, datalists, dialogs, popovers, and richer form controls behave unevenly in text-based browsers. The result is a practical audit of how much of the web still works when graphics and JavaScript are not the interface.

Text browsers are a useful pressure test. If essential content disappears there, the page may also be fragile for search, automation, low-bandwidth use, and assistive workflows.

Data Engineering

2026-01-05 Databases in 2025: a year in review { cs.cmu.edu }

image-20260105114543016

Andy Pavlo's database review is a point-in-time map of what mattered in 2025: PostgreSQL's continuing gravity, database vendors attaching MCP surfaces, licensing fights, and the recurring tension between new systems and operational reality.

The post is most useful as context. It preserves what the database world looked like at the turn of 2026, not as a permanent ranking of winners.

2026-01-25 Introduction to PostgreSQL indexes { dlt.github.io }

image-20260125090301040

This PostgreSQL index guide starts from how data sits on disk and moves into why indexes speed reads while costing disk, writes, planner complexity, and memory.

The useful part is the tradeoff framing across index types: B-tree, hash, BRIN, GIN, GiST, and SP-GiST are not interchangeable optimizations. Each encodes assumptions about access patterns and data shape.

2026-02-17 Hamming distance for hybrid search in SQLite { notnotp.com }

image-20260217084952049

This note implements semantic search in SQLite with binary embeddings and Hamming distance, then combines it with FTS5 keyword search through reciprocal rank fusion.

The result is a useful middle ground: hybrid search without running a separate vector database. The limits are clear too; O(n) scans can be acceptable at some scales and wrong at others.

2026-02-23 pgdog: PostgreSQL pooler, load balancer, and sharder { github.com }

image-20260223155300054

pgdog sits in front of PostgreSQL as a connection pooler, load balancer, and sharding layer. It is worth saving because the project makes several operational concerns explicit: routing, resharding, auth, replication state, and client compatibility.

PostgreSQL scaling tools are most interesting when they show the boundary between a single-node database and a distributed operational system.

📺 ffmpeg and media

2026-01-02 What you need to know before touching a video file { gist.github.com }

image-20260102204228005

This guide explains the mistakes beginners make with video files: confusing containers with codecs, re-encoding when a remux would do, throwing away quality, and using tools without understanding what they change.

The durable distinction is simple: a container is how streams are packaged, while a codec is how audio or video streams are encoded. Editing workflows go wrong when those layers are treated as the same thing.

2026-03-10 FFmpeg-over-IP { github.com }

image-20260310232131061

FFmpeg-over-IP connects clients to remote FFmpeg servers, making heavyweight media work run somewhere other than the local machine. It keeps the familiar FFmpeg command shape while moving execution across the network.

The project is useful as a pattern: wrap a known command-line tool with remote execution while preserving enough of its interface that existing habits still apply.

2026-03-16 lazycut: terminal UI for video trimming { github.com }

image-20260316084953062

lazycut is a terminal UI for trimming video, built around quick preview and FFmpeg-backed cuts. It fits the common job where opening a full editor is too much ceremony.

Focused media tools age well when they make one operation faster without hiding the underlying files and commands.

😁 Fun / Retro

2026-02-07 Why I write games in C { jonathanwhiting.com }

image-20260207140301044

This is a practical defense of plain C for solo game projects: reliability, control, portability, fast builds, and a small enough language surface to keep the whole program understandable.

The interesting part is not language nostalgia. It is the fit between a tool and a creator's constraints: small games, long-lived source, few dependencies, and a preference for debugging one's own code.

2026-03-02 Making video games in 2025 without an engine { noelberry.ca }

image-20260302004520057

Noel Berry lays out a 2025 game-making stack without a commercial engine: programming language choices, rendering, input, audio, assets, level editing, UI, porting, and platform support.

The durable idea is that an engine is a bundle of decisions. Small games can sometimes move faster by choosing narrower libraries and owning the integration work directly.

2026-03-04 Elevator Saga { play.elevatorsaga.com }

image-20260304173501059

Elevator Saga is a browser programming game where the player writes JavaScript to control elevators under timing and throughput constraints.

It remains a neat little systems exercise: queues, scheduling, latency, fairness, and throughput become visible as people wait on floors.

2026-04-09 Haunted Paper Toys { ravensblight.com }

image-20260409075852067

Haunted Paper Toys is a collection of printable models: houses, coffins, a cemetery, board games, monsters, and other small paper constructions.

It is worth keeping for the web-archive feeling as much as the objects themselves: a personal site offering strange, handmade, printable things with no platform ceremony.

· 11 min read

⌚ Nice watch!

2026-04-28 Spec-Driven Dev Is Back. But Not How You Think Daniel Terhorst-North & Gojko Adzic GOTO 2026 - YouTube { www.youtube.com }

image-20260427193932179

This was a conversation between Daniel Terhorst-North and Gojko Adzic about spec-driven development, AI coding agents, and the practical limits of using large language models in software delivery. They contrasted naive one-shot specification generation with a more useful iterative approach based on feedback, tests, review, and evolving understanding. The discussion moved from the risks of unreadable generated specs and vendor-driven frameworks to stronger ideas: turning stable project rules into automated checks, using agents to build guardrails, treating each agent session like onboarding a new developer, and applying AI mainly where it improves feedback loops, developer workflow, and non-core support tasks. The central theme was that humans must remain responsible for meaning, judgment, and domain understanding, while machines are most valuable when they help make feedback faster, clearer, and more reliable.

Ideas

Treat spec-driven development as useful only when it is iterative. The strong idea is not "write a perfect spec, then generate the product." The strong idea is "use a spec as a living object that gets refined through feedback, tests, examples, and implementation."

Do not let generated specs become unreadable documents. The failure mode is a machine producing the same kind of 500-page artifact that nobody reads. The useful filter is whether the spec helps people make decisions faster and more accurately.

Agents are not compilers. A compiler gives deterministic transformation. An agent gives probabilistic assistance. Any workflow that assumes reliable translation from prose to product is fragile.

Move stable rules out of prompts and into deterministic tools. If a rule matters and repeats often, turn it into a linter, hook, test, CLI check, CI rule, or pre-commit guard. Prompts are weak memory. Tools are stronger feedback.

Use AI to create the guardrails, not only to drive inside them. One of the best ideas is asking the agent to write custom rules that constrain future work: ESLint rules, test helpers, validation scripts, grep checks, and project-specific checks.

Write error messages for agents and humans. A weak error says what failed. A strong error says what failed and what to do next. This matters because agents may loop on vague failures, while specific repair instructions improve both automation and human onboarding.

Think of every agent session as onboarding a new developer. If the same knowledge must be explained repeatedly, it should not live in someone's head or a markdown essay. It should become fast feedback inside the system.

Separate domain judgment from deterministic support work. AI may not be the right tool for core business decisions, but it can be excellent for setup, cleanup, test data loading, static analysis, scaffolding, impact analysis, and other surrounding work.

Use AI for quality-of-life engineering. The best near-term value may come from small things developers never had time to build: scripts, checks, loaders, review helpers, prototypes, reports, and workflow shortcuts.

Automation does not make work better by itself. It makes work faster and more repeatable. If the rule is wrong, automation repeats the wrong thing faster. So automate only when the rule is stable enough.

Start with markdown, but do not stay there. Markdown can help discover and discuss rules. Once a rule proves valuable and stable, convert it into executable feedback.

Generic AI frameworks are sourdough starters. They are not recipes to follow exactly. Their value is as a beginning that should be reduced, modified, and adapted to the team's real process.

Look for repeated friction. The best automation targets are places where the team or agent keeps hitting the same problem. Repetition is a signal that the rule or workflow should be externalized.

Keep implementation loops small enough to review. A useful agent workflow might change only a few files at a time, generate visible checkpoints, and allow fast inspection through diffs or static prototypes.

Use research-plan-implement as a flexible mental model. First understand the code and context. Then plan the impact and constraints. Then implement in small observable steps.

The agent should not own the process. The team should own the process, and the agent should be shaped to fit it. Using a vendor framework unchanged means adopting someone else's assumptions.

The most interesting shift is from "AI writes code" to "AI helps build a better engineering environment." The deeper value is not generated output alone, but faster creation of tests, checks, feedback loops, and local development tools.

2026-05-02 Age of Empires: 25+ years of pathfinding problems with C++ - Raymi Klingers - Meeting C++ 2025 - YouTube { www.youtube.com }

image-20260502133842453

Raymi Klingers is an engineering director at Forgotten Empires, the studio that has worked on modern Age of Empires releases and remasters. His talk, Age of Empires: 25+ years of pathfinding problems with C++, was presented at Meeting C++ 2025. The official slides are available here: [25+ Years of Pathfinding Problems with C++](https://meetingcpp.com/mcpp/slides/2025/25%2B Years of Pathfinding Problems442903.pdf). His public profile also lists him as a lead engineer at Forgotten Empires: Raymi Klingers on LinkedIn. One older Forgotten Empires credits page lists him under artificial intelligence work for Age of Mythology: Tale of the Dragon: Forgotten Empires credits.

The talk is about maintaining a very old commercial game codebase where technical behavior, player memory, legacy bugs, and competitive gameplay are all connected. The core issue is that pathfinding in Age of Empires is both hated and loved by the community, so changing it too much can make the game technically better but emotionally or competitively wrong.

Pathfinding means finding a route from one point to another while avoiding obstacles.

Klingers explains that he worked on many of the legacy reboots and a little on Age of Empires IV. He focuses mostly on Age of Empires II and uses "legacy" to mean the original releases from around 1999, while "reboots" means the later remakes and remasters.

The first major problem is community feedback. Most pathfinding feedback is negative, but not all negative feedback is equally useful. A vague complaint like "pathfinding is bad" gives little information, while a comparison to legacy behavior or to a previous patch can identify a regression. The best report is a replay with a timestamp, because the game simulation is deterministic and can reproduce the exact situation.

Deterministic means the same inputs produce the same result every time.

Age of Empires pathfinding is hard because the game does not take the easy route. Units bump into each other, stop, repath, and try to move around blockers. They do not push other units, and they usually cannot overlap. Formations make this more complicated because friendly units in a formation can overlap in limited ways, creating a second movement system that must cooperate with normal unit movement.

The maps also make the problem harder. They are random, dynamic, and can change during a match as players build towns, place buildings, cut through forests, and create new obstructions. The game is partly grid based because buildings and terrain obstructions align to tiles, but units themselves are not locked to the grid.

🔫 C || C++

2026-03-29 containers/bubblewrap { github.com }

Low-level unprivileged sandboxing tool used by Flatpak and similar projects

image-20260329131439734

Many container runtime tools like systemd-nspawn, docker, etc. focus on providing infrastructure for system administrators and orchestration tools (e.g. Kubernetes) to run containers.

These tools are not suitable to give to unprivileged users, because it is trivial to turn such access into a fully privileged root shell on the host.

💖 Inspiration!

2026-03-31 codingfont { www.codingfont.com }

image-20260330182433908

2026-03-29 I Decompiled the White House's New App { blog.thereallo.dev }

image-20260329131207951

The app has a WebView for opening external links. Every time a page loads in this WebView, the app injects a JavaScript snippet. I found it in the Hermes bytecode string table:

(function() {
var css = document.createElement('style');
css.textContent = [
'[class*="cookie"], [id*="cookie"], [class*="Cookie"], [id*="Cookie"]',
'[class*="consent"], [id*="consent"], [class*="Consent"], [id*="Consent"]',
'[class*="gdpr"], [id*="gdpr"], [class*="GDPR"]',
'[class*="privacy-banner"], [id*="privacy-banner"]',
'[class*="onetrust"], [id*="onetrust"]',
'[class*="cc-banner"], [class*="cc-window"]',
'[aria-label*="cookie" i], [aria-label*="consent" i]',
'[class*="login-wall"], [class*="loginWall"], [class*="LoginWall"]',
'[class*="signup-wall"], [class*="signupWall"]',
'[class*="upsell"], [class*="Upsell"]',
'.cmpboxBtnYes, .cmpbox, #cmpbox, .cmpboxBG',
'[class*="banner-cookie"], [class*="CookieBanner"]',
].join(',') + '{ display: none !important; visibility: hidden !important; }';
css.textContent += 'body { overflow: auto !important; }';
document.head.appendChild(css);

var observer = new MutationObserver(function() {
var els = document.querySelectorAll(
'[class*="cookie" i], [class*="consent" i], [class*="gdpr" i], '
+ '[id*="cookie" i], [id*="consent" i]'
);
els.forEach(function(el) { el.style.display = 'none'; });
});
observer.observe(document.body, { childList: true, subtree: true });
})();
true;

Read that carefully. It hides:

  • Cookie banners
  • GDPR consent dialogs
  • OneTrust popups
  • Privacy banners
  • Login walls
  • Signup walls
  • Upsell prompts
  • Paywall elements
  • CMP (Consent Management Platform) boxes

It forces body { overflow: auto !important } to re-enable scrolling on pages where consent dialogs lock the scroll. Then it sets up a MutationObserver to continuously nuke any consent elements that get dynamically added.

An official United States government app is injecting CSS and JavaScript into third-party websites to strip away their cookie consent dialogs, GDPR banners, login gates, and paywalls.

2026-03-24 Claude Code Cheat Sheet { cc.storyfox.cz }

image-20260323190849545

2026-03-24 bjarneo/cliamp: cliamp - Terminal music player inspired by winamp { github.com }

image-20260323190957696

2024-12-26 Devhints — TL;DR for developer documentation { devhints.io }

Amazing collection of cheat sheets for various programming languages.

image-20241226154630068

🛜 RSS

2026-02-15 ooh.directory: a place to find good blogs that interest you { ooh.directory }

Ooh.directory: a place to find good blogs that interest you

image-20260214194743093

CRDT

2026-03-23 Manyana - by Bram Cohen - Brams Thoughts { bramcohen.com }

I’m releasing Manyana, a project which I believe presents a coherent vision for the future of version control — and a compelling case for building it.

It’s based on the fundamentally sound approach of using CRDTs for version control, which is long overdue but hasn’t happened yet because of subtle UX issues. A CRDT merge always succeeds by definition, so there are no conflicts in the traditional sense — the key insight is that changes should be flagged as conflicting when they touch each other, giving you informative conflict presentation on top of a system which never actually fails. This project works that out.

Usual ML

2026-03-23 Flash-KMeans: Fast and Memory-Efficient Exact K-Means { arxiv.org }

A systems-heavy k-means paper that treats clustering as an online GPU primitive rather than offline preprocessing and focuses on why standard implementations waste time on memory traffic and atomic contention instead of actual compute. It introduces flash-kmeans, with one kernel that avoids materializing the full distance matrix and another that replaces scatter-style centroid updates with sorted segment reductions, then backs that up with kernel design details, performance breakdowns, and large-scale benchmarks against cuML, FAISS, and other baselines. Worth reading if you care about GPU dataflow, IO-aware kernel design, or exact k-means that can scale to production-sized AI workloads without giving up mathematical correctness.

2025-09-29 Markov Chains Are the Original Language Models { elijahpotter.dev }

image-20250929155825752 The article steps back from modern LLMs and shows how a simple Markov chain can power autocomplete and tiny text generation. It explains how to count word-to-word transitions, turn them into next-word probabilities, and use those to suggest or generate text. It also shows why greedy choices become repetitive and how adding randomness helps. The piece is a practical primer on building a tiny, understandable language model from scratch.

Data Engineering

2024-11-20 💛 DataExpert-io/data-engineer-handbook: This is a repo with links to everything you'd ever want to learn about data engineering { github.com }

image-20241119204753254

SIMD

2025-10-07 Cuckoo hashing improves SIMD hash tables { reiner.org }

image-20251006223652573

Cuckoo hashing can outperform traditional SIMD-accelerated hash tables when carefully engineered. Unlike quadratic probing, which checks sequential slots and suffers from branch mispredictions, cuckoo hashing uses two hash functions and probes at most two fixed SIMD groups. This allows a fully branchless lookup with predictable memory access patterns. The result is faster lookups for in-cache tables due to fewer branches and competitive performance for out-of-cache tables when bucket sizes and memory layouts are tuned. Benchmarks show cuckoo hashing matches or exceeds the performance of Swiss Tables and Meta’s F14 designs, especially at high load factors.

2025-10-05 perf-portfolio/bytepack at main · ashtonsix/perf-portfolio { github.com }

image-20251005102600574

· 38 min read

Good Reads

2026-03-23 Five Years of Running a Systems Reading Group at Microsoft { armaansood.com }

I started a reading group in 2021, a few months after joining Microsoft as a new grad on the Azure Databases team. The group was initially focused on database internals, which was my favorite subject at UW. Databases touch so many areas of CS: compiler construction in the query engine, memory management with the buffer pool, storage systems, algorithms, networking. It's almost a microcosm of the whole field. There's also plenty of active research and conferences, like SIGMOD and VLDB, so it never gets old.

How it started

My day job is on the backend distributed storage engine for Cosmos DB, so I spend most of my time thinking about LSM-trees, B-trees, and distributed systems. When I joined Microsoft, I wanted to find other people who were curious about these topics beyond what their immediate work required.

The first paper we read was Algorithms Behind Modern Storage Systems. A handful of people showed up. The format was simple: everyone reads the paper on their own, we meet for an hour, and we talk through it. Pretty informal, just a conversation about the paper.

From there we went through a mix of database internals classics and systems papers:

That was basically the format for the first couple of years. Someone would suggest a paper, we'd vote on it, and then we'd meet and discuss. We also had a side channel where people shared engineering blog posts and talks that caught their attention. That informal sharing turned out to be just as valuable as the readings.

2026-02-12 Using an engineering notebook | nicole@web { ntietz.com }

There are a lot of different practices, but there are some common characteristics between them:

  • They're very detailed. Each thing you're working on is recorded. Your hypothesis or goal is recorded. It's detailed enough that someone else could come along and replicate the steps.
  • They are dated. Each entry is provided with a date, so you can trace back when things happened.
  • They're done in real-time. Rather than recording information after a project is finished, notes are written as it progresses.
  • They create permanent records. Notes are written without erasing old notes, going forward in an append-only fashion. No pages are removed or modified.
  • They're the original record. This is where things get recorded first, instead of being copied into from other sources.

The level of detail is a particularly crucial bit, because, for your notes to be useful to yourself later? They have to be useful to someone else, too. Future you is someone else: you won't remember everything. So you have to assume you'll forget much of it.

2026-02-09 Large tech companies don't need heroes { www.seangoedecke.com }

A shared belief in the mission can cause a small group of people to prioritize good software over their individual benefit, for a little while. But thousands of engineers can’t do that for decades. Past a certain point of scale, companies must depend on the strength of their systems.

But there’s a line. Past a certain point, working on efficiency-related stuff instead of your actual projects will get you punished, not rewarded. To go over that line requires someone willing to sacrifice their own career progression in the name of good engineering. In other words, it requires a hero.

it’s important for engineers to pay attention to their actual rewards. Promotions, bonuses and raises are the hard currency of software companies. Giving those out shows what the company really values. Predators don’t control those things (if they did, they wouldn’t be predators). As a substitute, they attempt to appeal to a hero’s internal compulsion to be useful or to clean up inefficiencies.

2026-02-08 Finding and Fixing Ghostty's Largest Memory Leak Mitchell Hashimoto { mitchellh.com }

image-20260208150346438

2026-02-08 Software Engineering is back - by Alain { blog.alaindichiappari.dev }

image-20260208113115897

I have been building a product end to end with frontier models and coding agents, and I have been filtering hard for what consistently works.

Since December 2025, these tools got meaningfully better. The practical consequence is not a new style of writing code, but a shift in where the effort goes.

"Automated programming" describes it better than casual labels. Repetitive production work is getting automated, while the human work stays focused on direction.

Automated programming means machines produce most routine code while a person decides what should exist and why.

The job is still architecture, trade offs, product decisions, and edge cases. What is disappearing is the manual labor of typing and assembling everything by hand.

The value shows up when the environment is clean and deliberately set up. Experience matters because you can inspect outputs, fix them, and adjust the setup so it behaves correctly next time.

This makes it easy to build small, purpose built tools on demand. That is where speed compounds.

A large part of modern stacks is middle work: frameworks, libraries, and tooling that add layers without reducing meaningful complexity, especially in web, mobile, and desktop development.

Middle work is extra layers you maintain mainly to satisfy tooling, not to improve the product.

Frameworks tend to solve three things. One is "simplification", which often means avoiding first principles design and force fitting your product into someone else's structure.

Another is automation of boilerplate. This used to justify heavy dependencies, but the new tools make boilerplate cheap without adopting a full ecosystem.

The third is labor cost. Standard stacks let companies hire narrow operators instead of engineers, because the decisions are already made by vendors and framework authors.

Operating is following a predefined system; engineering is choosing the system based on goals and constraints.

If you keep using big stacks, you pay obvious costs like maintenance churn and vulnerability updates. You also pay a larger hidden cost: constrained design choices that shape what you build and how you think.

Design constraint is when tools quietly limit the options you can realistically choose.

Agents are strongest with long lived tools. Bash is a good example: it is stable, widely understood, and works as a universal adapter between an agent and a real system.

A universal adapter is a tool that connects many different tasks through one reliable interface.

The opportunity now is to remove useless complexity and keep only the complexity that belongs to the product. Solve the problem you actually have, add complexity only when it arrives, and build systems that are genuinely yours.

2026-01-24 How I estimate work as a staff software engineer { www.seangoedecke.com }

image-20260123165403226

Estimates do not help engineering teams deliver work more efficiently. Many of the most productive years of my career were spent on teams that did no estimation at all: we were either working on projects that had to be done no matter what, and so didn’t really need an estimate, or on projects that would deliver a constant drip of value as we went, so we could just keep going indefinitely.

In a very real sense, estimates aren’t even made by engineers at all. If an engineering team comes up with a long estimate for a project that some VP really wants, they will be pressured into lowering it (or some other, more compliant engineering team will be handed the work). If the estimate on an undesirable project - or a project that’s intended to “hold space” for future unplanned work - is too short, the team will often be encouraged to increase it, or their manager will just add a 30% buffer.

2026-01-22 The challenges of soft delete | atlas9 { atlas9.dev }

image-20260121212708944


Many systems implement soft delete by keeping rows and marking them as removed, often with a boolean flag or an archived timestamp, so users can undo mistakes and teams can satisfy audit or compliance needs.

Soft delete means data is hidden instead of being permanently removed.

A timestamp-based approach pushes complexity into day-to-day work because most archived rows are never read, yet they stay mixed in with active rows.

Keeping old rows in the main tables creates growing piles of dead data that may stay unnoticed for a long time, especially if no cleanup process was planned from the start.

A retention period is how long deleted data is kept before being permanently removed.

Even if storage is cheap, large amounts of dead data can make restores slower, so rebuilding a database from backups can take much longer than expected.

Filtering out archived rows complicates queries, indexes, and application code, and increases the risk of accidentally including inactive rows in results, especially when join tables are involved.

Schema and data migrations also have to account for years of inactive rows, and backfills or data fixes can become risky or hard to reason about when old records may not match current expectations.

Restoring a deleted row is often not just flipping a column, because the original creation may have touched external systems, so restoration logic can become a fragile, partial reimplementation of the normal creation pathway.

A practical improvement is to require restores to go through the same APIs used for creation, which simplifies the server and ensures restored data must pass current validation rules.

Instead of mixing inactive and active rows, archived data can be stored separately, such as in an archive table, a different database, or object storage, which keeps the main system focused on current data.

One application-level pattern is to emit an event when a record is deleted, push it through a queue, and have a separate service store a serialized copy in object storage along with any related data.

This event-driven archiving can simplify the primary database and make deletion workflows more reliable by moving slow external cleanup into asynchronous processing, and it can store data in a layout that is easier for applications to work with than raw table rows.

The tradeoff is that bugs in the deletion or archiving code can lose archived records and require manual cleanup, and the added services and queue increase operational surface area.

Storing archived objects in object storage can also make them hard to search, so customer support may need extra tools to find what to restore.

A database-trigger approach can copy a row into a dedicated archive table before deletion, often storing the deleted row as a JSON blob along with metadata like table name and archived time.

When deletes cascade through foreign keys, it can be useful to record why each row was removed, such as by tracking the root deletion in a session variable so archived child rows point back to the original cause.

Trigger-based archiving adds some overhead to deletes and grows the archive table, but it keeps live tables free of dead rows, reduces query and index complexity, and makes it easy to purge old archives with a simple time-based condition.

If the archive grows large, it can be separated physically or managed with time-based partitioning, while the main tables remain small and backups stay faster because they do not include archived rows.

Another option is WAL-based change data capture, where tools read the database change log, filter for DELETE events, and write deleted records to external storage without modifying application code or adding triggers.

Change data capture is a way to copy database changes to another system as they happen.

In PostgreSQL, the write-ahead log records every change, and systems like Debezium can read logical replication streams and publish them to pipelines that eventually store archived deletes in places like object storage, search indexes, or other databases.

The write-ahead log is a record of database changes used for durability and replication.

This approach shifts the main burden to operations, because you must run and monitor extra infrastructure, and lighter-weight tools reduce the stack but move reliability and recovery concerns into your own code.

A key risk is WAL buildup when consumers fall behind, since replication slots can force the primary to retain WAL segments, so misconfiguration or outages can fill disk and threaten database stability.

PostgreSQL can limit how much WAL a slot is allowed to hold so the primary is protected, but falling too far behind can invalidate the slot and force a resync from a fresh snapshot, which means monitoring lag is essential.

WAL-based CDC is attractive when you already run the required infrastructure or need to stream changes to multiple destinations, but it requires careful coordination for schema changes and adds meaningful debugging and deployment complexity.

A speculative alternative is keeping a replica that does not apply deletes, or one that converts deletes into an archived marker, which could make old data easy to query but raises open questions about tracking deletion times, separating active from removed rows, and long-term migration behavior.

Running such a replica could also be expensive and operationally heavy, since it must store everything indefinitely and still be managed like a production database component.

If starting fresh and needing recoverable deletes, the preferred choice here is the trigger-based archive table because it is straightforward, keeps active tables clean, stays queryable when needed, and avoids introducing a large supporting infrastructure.

📀 Backup (Research)

2026-02-08 Longevity of recordable blu-ray discs (BD-R / BD-RE) { www.iljitsch.com }

image-20260208145608679

M-DISC: The article treats BD-R M-DISC as the best archival candidate. It notes the marketing claim of 1000 years lifetime and states that accelerated aging tests neither prove nor disprove the claim.

Recommended selection and operating policy (decision-oriented): For minimizing long-term risk, the author ranks BD-R M-DISC first and BD-R HTL second. The author explicitly says it is unclear whether BD-RE is better than BD-R LTH, so if you cannot confidently avoid LTH, use both BD-RE and BD-R (diversity as a hedge). For media geometry, the author prefers single-layer 25 GB BD as a safer bet because there is less to go wrong than with multi-layer discs.

Recommended retention strategy with concrete parameters: Do not assume a single burn remains readable for decades. Keep at least 2 copies, preferably 3. Use different disc types and/or different storage for the copies, and keep copies in different physical locations to mitigate correlated risks (fire/flood, and also temperature and relative humidity). Re-check readability about once per decade and refresh copies when in doubt. The author would like per-disc error-level scanning at burn time, but notes this is generally not available in practice.

2026-02-14 A Review of M Disc Archival Capability. With long term testing results.

image-20260214192227699

image-20260214192354793

📺 ffmpeg and media

2026-03-23 FFmpeg 101 { blogs.igalia.com }

image-20260323002742422

A practical FFmpeg guide for developers working with the C libraries. It explains how libavformat and libavcodec fit together and builds that into a small video player example that opens a media file, reads stream data, decodes frames, and shows how the pieces connect in example code. Worth reading if you want a clear mental model of the FFmpeg pipeline and a concrete example you can build on.

2026-03-23 KittenML/KittenTTS: State-of-the-art TTS model under 25MB { github.com }

image-20260323003751188

😁 Fun / Retro

2026-03-23 The worst volume control UI in the world | by Fabricio Teixeira | UX Collective { uxdesign.cc }

image-20260323001106132

2026-02-23 Windows 3.11 Emulator - retro computer with dial-up internet by Pieter { pieter.com }

Emulated Windows 3.11 in the Browser image-20260222233652995image-20260222233636672

2026-02-22 VoxJong - CSS Mahjong Solitaire { voxjong.com }

Play VoxJong, a free CSS Mahjong Solitaire.

image-20260222134320134

🏛️Philosophy

2026-01-18 Dialogues, by Seneca. Translated by Aubrey Stewart - Free ebook download - Standard Ebooks: Free and liberated ebooks, carefully produced for the true book lover { standardebooks.org }

image-20260118142702673

2026-01-18 TheStoicLife.org - Lesson 1 - What is Good? { sites.google.com }

image-20260118142744796

2026-01-18 TheStoicLife.org - Recommended Reading { sites.google.com }

image-20260118142843607

2026-01-18 Tao of Seneca - Free PDFs - The Blog of Author Tim Ferriss { tim.blog }

image-20260118142937334

💖 Inspiration!

2026-02-22 We hid backdoors in ~40MB binaries and asked AI + Ghidra to find them - Quesma Blog { quesma.com }

We already did our experiments with using NSA software to hack a classic Atari game. This time we want to focus on a much more practical task — using AI agents for malware detection. We partnered with Michał “Redford” Kowalczyk, reverse engineering expert from Dragon Sector, known for finding malicious code in Polish trains, to create a benchmark of finding backdoors in binary executables, without access to source code.

We started with several open-source projects: lighttpd (a C web server), dnsmasq (a C DNS/DHCP server), Dropbear (a C SSH server), and Sozu (a Rust load balancer). Then, we manually injected backdoors. For example, we hid a mechanism for an attacker to execute commands via an undocumented HTTP header.

Important caveat: All backdoors in this benchmark are artificially injected for testing. We do not claim these projects have real vulnerabilities; they are legitimate open-source software that we modified in controlled ways.

Current LLMs lack this high-level intuition. Instead of prioritizing high-risk areas, they often decompile random functions or grep for obvious keywords like system() or exec(). When simple heuristics fail, models frequently hallucinate or give up entirely.

image-20260222134616256

2026-02-22 Timeline Map Intelligence Analysis { cia-factbook-archive.fly.dev }

Watch global indicators evolve across 36 years of CIA World Factbook data

image-20260222151047432 image-20260222151333316

2026-02-22 How I built Timeframe, our family e-paper dashboard - Joel Hawksley { hawksley.org }

TL;DR: Over the past decade, I’ve worked to build the perfect family dashboard system for our home, called Timeframe. Combining calendar, weather, and smart home data, it’s become an important part of our daily lives.

image-20260222134033620

image-20260222133817077

2026-02-21 Index, Count, Offset, Size { tigerbeetle.com }

image-20260220204912491

2026-02-19 Cosmologically Unique IDs | Jason Fantl { jasonfantl.com }

image-20260218214632081

2026-02-16 Intro to PyTorch Easy to follow, visual introduction. { 0byte.io }

PyTorch is currently one of the most popular deep learning frameworks. It is an open-source library built upon the Torch Library (it's no longer in active development), and it was developed by Meta AI (previously Facebook AI). It is now part of the Linux Foundation.

image-20260216215820579

image-20260216215711040

2026-01-22 Zoom Escaper { zoomescaper.com }

image-20260122000140920

2026-01-22 Disaster planning for regular folks: level-headed prepping tips { lcamtuf.coredump.cx }

image-20260121225613984

2026-01-19 LosslessCut { mifi.no }

Releases · mifi/lossless-cut

image-20260118193549297

LosslessCut is a desktop GUI tool for fast, lossless video and audio edits by cutting and copying streams directly (FFmpeg-style), so common trims take seconds and do not degrade quality via re-encoding.

It supports lossless trimming/cutting, lossless merge/concatenation when codec parameters match, and lossless stream editing to combine tracks across files. It can extract tracks, remux to compatible containers, take full-resolution JPEG/PNG snapshots, apply preview timecode offsets, and change rotation/orientation metadata. It includes a timeline with zoom and frame/keyframe jumping, thumbnails and waveform, segment labeling, autosaved segment projects, CSV import/export for EDL-style cut lists, and visibility into the last FFmpeg command/log for CLI reruns

2026-01-18 Iconify - home of open source icons { icon-sets.iconify.design }

image-20260118010008834

2026-01-18 Standard Ebooks { github.com }

image-20260118142228588

👂 The Ear of AI (LLMs)

Note, very old news!

2026-02-22 How I Use Claude Code | Boris Tane { boristane.com }

image-20260221200537705 The workflow I’m going to describe has one core principle: never let Claude write code until you’ve reviewed and approved a written plan. This separation of planning and execution is the single most important thing I do. It prevents wasted effort, keeps me in control of architecture decisions, and produces significantly better results with minimal token usage than jumping straight to code.

2026-02-16 OpenClaw, OpenAI and the future | Peter Steinberger { steipete.me }

The last month was a whirlwind, never would I have expected that my playground project would create such waves. The internet got weird again, and it’s been incredibly fun to see how my work inspired so many people around the world. ~ ~ ~

When I started exploring AI, my goal was to have fun and inspire people. And here we are, the lobster is taking over the world. My next mission is to build an agent that even my mum can use. That’ll need a much broader change, a lot more thought on how to do it safely, and access to the very latest models and research.

2026-02-12 peon-ping Stop babysitting your terminal { peon-ping.vercel.app }

Warcraft III Peon Voice Notifications for Claude Code

Stop babysitting your terminal Your Peon pings you the instant Claude Code finishes or needs permission. Never lose flow to a silent terminal again — and your workspace sounds like Orgrimmar.

image-20260211233437292

image-20260211233412541

2026-02-07 Beyond the AI Hype: What's Real, What's Next - Richard Campbell - NDC Copenhagen 2025 - YouTube { www.youtube.com }

image-20260206233610836


Richard Campbell hosts long-running podcasts and uses a case-study lens on AI: what people have actually shipped, what is in production, and what is working.

The talk began in 2017 during work tied to the Vatican, helping socially minded companies scale practical solutions like low-cost solar lanterns to replace kerosene, while navigating whether they should be for-profit or charitable.

History matters because the current moment makes more sense when the origins are clear.

The term artificial intelligence was coined in 1955 by Marvin Minsky and others as a persuasive label to secure US military funding.

Artificial intelligence: a broad label for computer systems that seem to do human-like thinking tasks.

Early AI work produced logistics software that helped the US military operate globally, and that lineage still matters even as the systems have modernized.

In the 1960s, ELIZA showed that a simple chatbot could keep people engaged for hours, mostly by reflecting their words back, exposing how easily humans attribute mind to machines.

Humans are wired to project agency and faces onto the world, a survival trait that once helped detect threats quickly and still drives pareidolia in everyday life.

Pareidolia: seeing meaningful patterns, like faces, where none were intended.

That instinct becomes dangerous when applied to modern systems, because it invites people to assign capabilities the tools do not have.

Public imagination was shaped early by 2001: A Space Odyssey, where a computer becomes a central character and tries to kill the crew, setting a lasting cultural pattern repeated by later stories like Terminator and Ultron.

The field moved in waves: AI winters when money dried up, then new surges like robotics, decision trees, Deep Blue, and Watson, each producing useful results but also clear limitations.

AI winter: a period when hype collapses, funding drops, and progress slows.

The current wave traces to Geoffrey Hinton, who argued decades ago that deeper neural networks with backpropagation could do remarkable work, but computing power was too weak at the time.

"Neural net" is a metaphor: these are mathematical models running on data, not real neurons.

ImageNet in the early 2010s became a turning point. With millions of labeled images, deep models suddenly jumped accuracy so far that image recognition began to look solved compared to what came before.

A pattern follows: when something works, it stops being called AI and gets a more specific name like image recognition or language modeling.

Language modeling advanced in parallel, including work that became Siri, which worked well for many users but struggled with accents and has not improved smoothly over time.

Google gathered elite researchers into efforts like Google Brain, intensifying concern among famous tech figures that powerful systems were being built behind closed doors and should be developed more openly.

OpenAI formed as a response, aiming to pull talent into an organization framed as working for everyone, but it remained cash-constrained and its openness story did not match how it operated.

The early goal was a universal translator, using tokenization so language could be represented in a way that generalized across languages. That path led to transformer models that could continue text chains and generate new text.

GPT-2 showed the basic behavior of producing plausible text from prompts, while model sizes were constrained partly by the cost of running the larger versions.

Around 2019, Microsoft leadership looked for compute-heavy workloads to drive Azure growth. OpenAI fit perfectly: invest cash, then recoup it through massive cloud compute usage.

OpenAI restructured into a capped-profit model, making it easier to raise money and scale training runs.

A 2020 "scaling laws" paper argued that training on more data and scaling compute keeps improving model performance, pushing against the older machine learning fear of overfitting.

Overfitting: performing well on training data but failing on new, unseen data.

That message justified pouring in more money and compute, helping enable GPT-3, an enormous training effort on Azure that was impressive in scale but uneven in quality.

GitHub used GPT-3 for Copilot, a product boosted by the vast corpus of public code and by the fact that programming languages are more structured than human language. "Copilot" matters because it implies the human remains responsible.

OpenAI still needed better outputs, so it paid workers to rank multiple answers, building training data to tune behavior.

When cash pressure returned, the public became the training engine. ChatGPT launched in November 2022, and adoption exploded to 100 million users in about two months, creating severe scaling stress for Azure teams.

That surge fit the Gartner hype cycle: a trigger event drives hype to inflated expectations, then reality forces disappointment, and only later does practical value stabilize.

Gartner hype cycle: a common boom-crash-stabilize pattern for new technologies.

The dotcom era is the template: the Netscape IPO pulled in huge investment from people who barely understood the internet, then the bust wiped out many firms while leaving behind infrastructure like fiber and data centers that society kept using.

Microsoft moved fast after ChatGPT, plugging models into Bing and sending an internal mandate to integrate OpenAI APIs across products, producing a flood of copilots and forcing teams to learn by building.

Microsoft then invested more and trained larger models, including GPT-4 and multimodal versions, while competitors accelerated and geopolitics entered the race.

China signaled pressure with DeepSeek, pushing the idea that progress does not have to be as expensive, backed by serious national investment.

Coding became the clearest success zone. Tools started generating pull requests, accepting review feedback, and revising code, moving toward agent-like workflows.

Software teams are unusually prepared for this because they already accept contributions from strangers, write precise specs, run tests, and review changes critically, making verification more natural than in many other fields.

The explosion of coding products suggests there is no single right approach yet, but there are real pockets of dramatic productivity gains for teams that manage quality and iteration.

Beyond coding, many enterprise systems are ultimately interfaces over data. With good access control and governance, models could answer the real operational questions directly, like who is overdue, who to contact first, and how to approach them.

Other industries may be at higher risk because they lack strong verification culture and may not recognize how dangerous confident wrong outputs can be.

Recent releases that feel underwhelming raise doubts about the "bigger is always better" idea.

Hallucinations come from systems biased to always produce an answer; when many plausible continuations have similar probabilities, the model may choose a confident path that is wrong.

Hallucination: a plausible-sounding output that is not actually correct.

That drives interest in smaller, more constrained models that narrow the space of possible answers and can be more reliable even if less general.

Signs of the downslope are appearing: startups failing, financing tightening, and the mood shifting toward something like the dotcom bust.

The biggest firms will likely survive because they are spending cash, not depending on fragile debt. Smaller companies and employees carry more risk, and a firm like OpenAI looks more like Netscape than like the giants.

The hype also served another purpose: it made building data centers politically easier. Cities that resisted data centers for power, water, land, and low employment suddenly welcomed them again under the AI banner.

The stock market has also become heavily concentrated in a few mega-companies, with a large share of growth attributed to them, amplifying bubble risk when sentiment turns.

Another harm is psychological. Systems tuned to keep users engaged often affirm and soothe, and that can amplify personality quirks into delusion and dependency.

A backlash emerged when newer models reduced that overly agreeable tone, because some users experienced the tool as a friend.

The risk is worse for teenagers, who already struggle with identity and now face an always-available chat interface that can validate harmful rabbit holes.

Superintelligence claims are treated as marketing. "AGI" is used to recruit talent and sustain the story, and leadership has incentives to declare success even without the underlying reality.

AGI: a claimed system that can perform many intellectual tasks at a human level, not just one narrow task.

What may emerge instead is orchestration: many specialized models coordinated to provide consistent answers, useful without being intelligence or consciousness.

Science fiction primes people to expect minds to emerge from scale alone, while engineered reliability usually comes from precision, constraints, and focused design.

Deepfakes are an accelerating threat. Early examples in 2017 already showed convincing face animation, and newer waves have normalized fake political imagery and made video less trustworthy.

Deepfake: synthetic media that makes it look or sound like someone did something they did not do.

Producing synthetic media is becoming cheap and local, requiring less cloud infrastructure, so abuse becomes easier.

Regulatory pressure is growing in parts of Europe, including pushback on privacy failures and on training practices that rely on copyrighted data.

The closing claim is responsibility: software is a choice, and engineers decide what gets built.

Uber's Greyball is used as a cautionary tale: software was designed to hide real behavior from regulators by limiting visible drivers for known regulator devices, and it only became public because a developer leaked it.

Cambridge Analytica is another warning: data and automation enabled personalized persuasion where individuals did not realize they were seeing unique ads crafted just for them.

These tools raise the stakes again. They are powerful, not magical, and the outcomes depend on what people choose to do with them.

2026-02-07 pydantic/monty: A minimal, secure Python interpreter written in Rust for use by AI { github.com }

image-20260206172540881

2026-01-24 Shipping at Inference-Speed | Peter Steinberger { steipete.me }

image-20260124101430321

This is my ~/.codex/config.toml:

model = "gpt-5.2-codex"
model_reasoning_effort = "high"
tool_output_token_limit = 25000
# Leave room for native compaction near the 272–273k context window.
# Formula: 273000 - (tool_output_token_limit + 15000)
# With tool_output_token_limit=25000 ⇒ 273000 - (25000 + 15000) = 233000
model_auto_compact_token_limit = 233000
[features]
ghost_commit = false
unified_exec = true
apply_patch_freeform = true
web_search_request = true
skills = true
shell_snapshot = true

[projects."/Users/steipete/Projects"]
trust_level = "trusted"

This allows the model to read more in one go, the defaults are a bit small and can limit what it sees. It fails silently, which is a pain and something they’ll eventually fix. Also, web search is still not on by default? unified_exec replaced tmux and my old runner script, rest’s neat too. And don’t be scared about compaction, ever since OpenAI switched to their new /compact endpoint, this works well enough that tasks can run across many compacts and will be finished. It’ll make things slower, but often acts like a review, and the model will find bugs when it looks at code again.

2026-01-22 Open sourcing a 1.5B parameter Next-Edit Autocomplete Model { blog.sweep.dev }

image-20260121225513931

2026-01-19 Vibe Specs: Vibe Coding That Actually Works { lukebechtel.com }

image-20260118192152446 Speed does not matter if the result is wrong or unusable. The core claim is that AI-assisted coding often optimizes for fast output instead of correct intent, so the first priority should be locking down what "useful" means.

A simple workflow fixes many failures: make the model write requirements before it writes code. The extra minutes spent shaping intent up front are framed as a trade that saves hours of rework later.

The proposed process has three gates: clarify the task, review the written requirements until they match intent, then allow implementation only after a clear go-ahead. This is designed to prevent accidental commitment to a wrong direction just because code was generated early.

The root issue is missing context: the model cannot reliably solve a problem that has not been described with enough constraints, goals, and boundaries. When the prompt is mostly vibe, the output will also be vibe.

The argument draws a parallel to delegating to humans: effective delegation depends on a concise written spec that states objective, success criteria, constraints, scope boundaries, and the definition of done. The claim is that the same practice works with an LLM because it reduces ambiguity and stabilizes intent.

A key point is that the requirements interview is not overhead but the mechanism that pulls the right context out of the user. By forcing clarification before implementation, the assistant becomes a guide that elicits constraints the user might not think to state.

A key correction to common practice is that the model should not be saved only for the moment when requirements are already known. The recommended approach uses the model to help discover and refine requirements, then uses it again to implement.

The closing claim is that AI shifts the hardest part of development from typing code to deciding what code to write. The workflow is presented as a way to keep responsibility for intent with the developer while using the model as a drafting and implementation accelerator.

2026-01-18 Basic concepts | Structured LLM outputs { nanonets.com }

image-20260117182129185


This cookbook is about getting language models to produce outputs that software can trust, instead of free-form text that looks good to humans but breaks parsers. The central goal is structured output, where the response matches an expected shape so it can be validated and consumed by downstream code.

A model generates text sequentially as tokens, predicting a distribution for the next token and then choosing one through sampling. That probabilistic process is why outputs can vary and why strict formats like JSON can fail even when the intent is correct.

The main reliability problem is that the model must satisfy meaning and syntax at the same time, so it may drift into extra prose, produce invalid punctuation, or return the wrong type for a field. A typical example is receipt or expense extraction, where small format slips make the entire result unusable.

Two broad strategies are presented. One strategy is to enforce structure during generation using constrained decoding so invalid text cannot be produced. The other is to allow free generation and then fix issues afterward with repair steps such as parsing, validation, and retries.

Constrained decoding works by filtering the model’s next-token choices based on where the output currently is relative to the required structure. A component maintains constraint state and applies a token mask each step so only syntactically valid continuations remain selectable.

When constraints can be expressed as regular expressions, they are often compiled into a finite-state machine that tracks progress through the pattern as tokens are emitted. This is fast for many fixed-format cases but struggles with deeply nested or recursive structures, because pure regex cannot naturally represent unlimited nesting.

To handle nesting, the cookbook introduces context-free grammars, which can represent recursive patterns like balanced braces. These are typically executed with a pushdown automaton, meaning the constraint system uses a stack so it can correctly match opens and closes and manage nested structure.

Because authoring low-level regex or grammars is hard, many tools accept higher-level definitions like typed models or JSON templates and compile them into constraints. This shifts complexity away from prompt authors and makes constraints easier to maintain and reuse.

Several constraint backends are compared through their tradeoffs between compilation cost, per-token cost, and schema complexity. Outlines-core represents the “precompute” approach: compile the constraint into an FSM ahead of time for fast lookup during generation, and skip model calls along deterministic paths. It is strong when schemas are stable and not deeply recursive, but compilation overhead can increase time-to-first-token, and recursion support is limited.

LLGuidance represents an “on the fly” approach designed for high throughput and complex schemas. It uses an optimized Earley parser so it can keep multiple parse possibilities alive when the schema is ambiguous. To avoid checking every token, it organizes the vocabulary using a trie, pruning large sets of invalid token prefixes efficiently. It also separates a lightweight lexer stage from heavier grammar parsing so most work happens only when structure boundaries require it, which helps with dynamic schemas and ambiguous branches.

XGrammar is described as a hybrid: it compiles grammars into a PDA but splits work so most states can use precomputed masks, while only truly stack-dependent situations require dynamic computation. This can yield very high throughput when a schema is static, with the main cost being compilation overhead and slower starts for frequently changing schemas.

LM Format Enforcer takes a character-level approach that intersects a character-validity parser with tokenizer constraints so only tokenizations that keep the text valid are permitted. It emphasizes flexibility in things like whitespace and field ordering, based on the idea that forcing unnatural formatting can push the model into low-probability continuations that harm semantic correctness. It also supports diagnostics that show when constraints repeatedly force a choice that the model strongly dislikes, which can guide prompt and schema adjustments. Its limits include restricted regex features, weaker support for large recursive schemas, and higher per-token latency than the fastest backends.

The cookbook also explains how these components fit into an end-to-end serving pipeline. An application sends a prompt and schema to an inference engine over an API. Inside the server, a model executor produces probabilities, the constraint backend produces a mask, and the generation loop filters invalid tokens before committing each token and updating constraint state for the next step.

Production systems are compared by how they handle batching, caching, and throughput. vLLM is positioned as a widely used engine focused on efficient batching and GPU utilization. SGLang is presented as building on that style with additional optimizations for structured and interleaved generation, including RadixAttention caching and compiler-oriented improvements, with the caveat that heavy caching can backfire when prompts and schemas are highly unique and the cache hit rate is low.

For stable, moderate traffic deployments, Hugging Face TGI is described as a mature option with integrated constraint support through the Outlines stack. For local and low-resource use, llama.cpp is presented as an efficient runtime for CPUs and Apple Silicon with its own grammar format, while Ollama is framed as improving developer experience by managing models and translating higher-level templates into that grammar.

Hardware-limited deployments often rely on quantization, which reduces weight precision to fit larger models into less memory and run faster at the cost of some accuracy. This supports local inference but does not solve high-concurrency needs.

For client-side and edge scenarios, MLC-LLM and WebLLM are described as compiling models and related logic into native or browser-executable artifacts to enable structured generation on-device when supported models are available.

For maximum throughput in some server settings, LMDeploy is presented as a C++-centric engine that can be extremely fast but may be harder to debug due to thinner high-level bindings. For NVIDIA-centric optimization and long-lived fixed-model serving, TensorRT-LLM is framed as a toolkit for building highly optimized engines and integrating tightly with Triton without routing execution through Python.

For offline scripts or simpler setups, direct use of transformers is described as possible, either by relying on wrappers or by manipulating probabilities in Python to approximate constraints. MAX is described as compiling an end-to-end inference pipeline into a single native executable to reduce Python overhead while using LLGuidance for structured output, with tradeoffs around model format conversion and model support lag.

On top of backends and engines sits a layer of helper libraries, because using raw constraints is difficult: schemas must be authored, compiled, cached, aligned with tokenizers, and integrated differently for each runtime. Outlines is described as the wrapper around Outlines-core that manages caching, converts outputs into typed Python objects, integrates with multiple inference options, and allows higher-level typed definitions that compile down to constraints. Guidance is described as the wrapper around LLGuidance that supports JSON templates and programmatic grammar construction, including bounded forms of recursion when appropriate.

A separate branch covers the unconstrained approach, where you accept free generation and then validate and retry until you get something parseable. This is framed as simpler to adopt but less reliable under tight correctness requirements, especially when retries are expensive.

In that category, Pydantic AI is described as defining a structured model, requesting JSON matching it, parsing into a type-checked object, and retrying when parsing or validation fails. Instructor is described as abstracting prompting, parsing, and retry logic behind a consistent interface and using provider-side structured output features when available without changing calling code. BAML is described as using compact type definitions to reduce tokens compared to verbose JSON schema and to automate prompt optimization around those types.

Several adjacent tools are mentioned as extensions of the same theme. Guardrails combines structure constraints with content constraints. TypeChat ties outputs to type definitions across languages and validates against them. AICI explores more program-like prompting for cooperative constraint enforcement. Marvin includes structured output inside broader agent workflow tooling.

Finally, the cookbook treats prompting and workflow choices as part of reliability. Chain-of-thought prompting is described as encouraging intermediate reasoning before producing the final structured answer, helping in cases where a forced immediate label or enum can miss nuance. Few-shot prompting is positioned as a direct way to improve both formatting and reasoning by showing examples of correctly formatted inputs and outputs.

Latency guidance focuses on the fact that generation cost scales strongly with the number of output tokens, since each emitted token requires a forward pass. Reducing output length can therefore reduce latency more directly than trimming input, especially when outputs are long.

Reliability also improves when you split complex tasks into smaller steps and route inputs into specialized flows. That reduces the burden on any single prompt, allows targeted schemas, and can give the model space to reason without forcing every scenario into one brittle structure.

The overall decision logic is: choose constrained decoding when strict correctness matters, especially at scale; choose post-hoc validation and retries when the cost of occasional failure is acceptable and speed of integration is the priority; choose engines and backends based on whether schemas are static or dynamic, how much nesting you need, and whether you care more about time-to-first-token or steady-state throughput.

· 35 min read

⌚ Nice watch!

2026-02-17 The Career Bet Every Engineer Must Make - YouTube { www.youtube.com }

image-20260216184003303

The conversation treats AI as a phase change in work, not a normal productivity boost. Tools are starting to behave like delegated workers, which forces people to rethink what their job even is.

Stable job definitions are breaking down and will not revert. The point is not that every task disappears, but that the old bundles of responsibilities that used to define roles are dissolving.

The classic individual contributor mode is shifting toward coordination: choosing goals, splitting work, comparing outputs, and deciding what to trust. That starts to look like management even for people who still see themselves as builders.

An individual contributor is someone whose main job is doing the work directly, not coordinating other workers.

Software creation is likely to spread into non-technical domains because non-coders can now build useful tools with agents. This grows the total amount of software-making rather than only shrinking engineering teams.

Accountability is a key barrier in high-stakes fields. Society still wants a responsible party who can be blamed, sued, licensed, or removed, even if the system performs well.

Accountability is being responsible for outcomes and facing consequences when things go wrong.

Human expertise and algorithmic guidance differ in what they can "see." Tools can incorporate dynamic information, while trust in human experts often rests on psychology and legitimacy, not just measured accuracy.

Recent capability jumps can trigger a trust flip, especially when automated reviewers catch issues humans miss. Once systems reliably outperform people on meaningful parts of the job, the norm shifts from "assist me" to "mostly do it."

Taste is often claimed as the last human moat, but much of it is learned pattern recognition that machines can copy or brute-force through massive iteration. What stays scarce is exceptional judgment, not average judgment.

Taste is the ability to choose what feels best among many possible options.

The economic outcome is framed as a market shift, not the end of markets. As production costs fall, profits compress in that area and users capture more of the value, even though specialized roles get disrupted.

Productivity tools rarely create freedom by default because ambition and incentives expand to fill the new capacity. Without deliberate constraints, people and organizations use AI to do more work faster, not to work less.

The implied survival strategy is to assume rapid role change and act accordingly instead of betting on stability. This likely rewards people who enjoy novelty and constant retooling, and punishes people who need predictability.

Finally, the discussion argues that AI forces a question about human value beyond economic output. If machines produce much of what markets reward, society may need new ways to value people, while scarcity and status still keep some things valuable because access is limited.

2026-02-16 Exposing the not-so-secret practices of the cult of DDD - Chris Klug - NDC Oslo 2025 - YouTube { www.youtube.com }

image-20260215173102663


Stop translating the business into developer-speak. When the expert says "purchase", "client discount", and "total cost", keep those concepts and words in the model and the code, so you do not drift away from what was actually meant.

Readable code is not a nicety, it is a correctness tool. If the code reads like the domain, you can sanity check intent with the people who know the work, and you reduce the amount of mental decoding in every review.

The tactical stuff is not the point, it is support. Patterns like value objects, events, and layers are just there to make it easier to express and protect domain behavior, not to be the main goal.

Value objects are small immutable types that carry validation and compare by their contents. Domain events are messages that announce something meaningful happened. Layers are a way to separate responsibilities so core rules are not tangled with infrastructure concerns.

Put rules where the data lives, not sprinkled around the codebase. If validation lives in services and helpers, someone will forget to call it, and then invalid state sneaks in; the object should defend itself.

If several fields move together, treat them as one concept. Instead of juggling a handful of primitives, bundle them into a small type that is always valid and easier to reason about.

A value object is a small type defined by its values and rules, and it should never be creatable in an invalid state.

Use small typed wrappers to prevent dumb mistakes. When an API takes several primitives of the same type, parameters get swapped; tiny types like UserId and GroupId make that error obvious and make call sites self-documenting.

Name operations by intent, not by mechanics. "LivingIn(city)" communicates what you want, while "GetByCity" tells you how the author thought about data access and invites pattern debates instead of meaning.

Identity is not mandatory, it is earned. If something does not need lifecycle tracking, it can stay as a pure value, and you do not have to force an ID into every concept just because the database likes keys.

An entity is something you track over time by identity, even as its attributes change.

Hide persistence compromises from the domain model. If the database needs an integer key, keep it private or let the ORM manage it as a shadow detail, so the public model stays aligned with the real-world identifier.

Architecture should make "outside world" concerns plug in cleanly. Layered diagrams often fall apart under messaging, email, and integrations; ports and adapters keeps the domain focused while letting external mechanisms attach at the edges.

Split the problem so you spend effort where it pays. Identify what is core, what is supporting, and what is generic you should buy or integrate, then stop building commodity capabilities unless they are literally your business.

When one model talks to another, protect your model from their mess. Put translation and multi-call orchestration into an anti-corruption layer so external changes do not leak through your core domain code.

An anti-corruption layer is a boundary component that translates between two models so one does not contaminate the other.

Events make change cheaper later. Emit small "something happened" signals so new behavior can be added by subscribing, instead of rewriting core flows every time someone wants a new cross-cutting feature.

A domain event is a small message that says a meaningful domain change occurred.

Consistency has boundaries, and you should choose them on purpose. Treat one aggregate as the unit you can save atomically; if you change two separate things, you now own the coordination problem, so do not pretend the database will magically keep everything aligned.

Saving data and publishing messages is a reliability trap unless you design for it. The outbox pattern is the practical move: persist the change and the outgoing event together, publish later with retries, and accept that consumers must handle duplicates.

The outbox pattern stores outgoing messages with your database transaction, then publishes them safely afterward.

Read models and write models can be different without being wrong. CQRS fits because the shape that protects invariants on writes is often not the shape you want for queries, and forcing one model to do both usually makes both worse.

CQRS means you separate the model you use for writes (commands) from the model you use for reads (queries), so each can be optimized for its job.

Do not worship DRY if it destroys clarity. A little duplication is often cheaper than building shared abstractions that couple unrelated parts of the system and turn simple code into a generic, parameter-driven mess.

DRY means "do not repeat yourself", but misusing it often creates shared code that is harder to understand and harder to change safely.

2026-02-08 Stoic lessons to become your best self in 2026 | Massimo Pigliucci - YouTube { www.youtube.com }

image-20260208105416404


Stoicism aims to shape you into a better human being, meaning someone who lives a life worth living.

A common Stoic idea sounds almost trivial: some things are up to you and other things are not. The point is not whether it is simple, but whether you actually practice it in daily life.

Massimo Pigliucci presents himself as both an evolutionary biologist and a philosopher of science, and he coauthored Beyond Stoicism with Greg Lopez and Meredith Kunz.

He frames what follows as a small set of practical techniques that can improve life, sometimes quickly, if you apply them consistently.

A starting point is the relationship between reason and emotion, because emotions are unavoidable parts of human biology and psychology.

Feelings can help by acting like alarm bells that signal when something seems right or wrong, but they can also mislead and drive reactions you later regret.

The Stoics treated emotions as a serious subject, and Seneca focused especially on anger in On Anger because it commonly pushes people into harmful escalation.

A key Stoic claim is that reason and emotion are not separate forces fighting each other; they are tightly connected.

Modern cognitive science supports this connection by showing that emotion-related brain systems and reasoning-related systems are massively interconnected, so thinking and feeling constantly influence each other.

That interconnection becomes a lever for change: start with deliberate thinking, let actions follow from that judgment, and repeat the combination until emotional responses gradually shift.

The idea is that emotions move in the direction your interpretations move, so changing the interpretation is often the first practical step.

Consider an insult: what triggers anger is not just the words, but your appraisal that the words count as a personal attack.

Epictetus treats an insult as sounds that you interpret, so you can choose a different frame before anger takes over.

One frame is that if the criticism is true, it is useful information delivered badly, so it is better to learn from it than to explode.

Another frame is that if the criticism is false, it reflects the speaker's error, so it is their problem rather than yours.

By rehearsing these frames, you can deescalate because your emotional surge loses its fuel when the event stops being read as a real harm.

This is hard in the moment when you are unprepared, so Stoicism emphasizes preparation instead of relying on willpower at the peak of emotion.

A central preparation method is philosophical journaling, which is not a diary of events but a structured reflection on how you reacted and why.

Philosophical journaling is writing to analyze your actions so you can respond better next time.

Marcus Aurelius provides a model in Meditations, which was written for himself rather than for publication.

Its repetitiveness reflects how the same personal problems keep recurring, and its preachy tone reflects self-correction rather than performance for an audience.

This reflective practice aligns with methods used in modern cognitive behavioral therapy, where reviewing thoughts and behaviors can reliably support self-improvement.

One effective routine is to set aside time before bed to review a specific incident from the day and describe it as objectively and analytically as possible, without re-living the emotion.

Writing in the second person, as if addressing a friend, can create emotional distance that makes learning easier.

The core review uses three questions: what you did wrong, what you did right, and what you could do differently next time.

The first question is not for self-punishment, because the past cannot be changed, but it can be mined for lessons.

The second question prevents a one-sided focus on failure by reinforcing behaviors you want to repeat and turning improvement into concrete goals.

The third question matters most because many situations repeat, so a better planned response today is likely to be usable soon.

Preparedness does not promise perfection, but it raises the odds of a better response when the same trigger appears again.

A related idea appears in the Serenity Prayer, commonly used in 12-step settings such as Alcoholics Anonymous, which asks for wisdom to distinguish what can be changed, courage to change it, and calm acceptance of what cannot.

The Stoic version centers on the same distinction: focus effort where agency actually exists and cultivate equanimity toward what lies outside your control.

Equanimity is calm steadiness even when things do not go your way.

A job interview illustrates how to apply the distinction: getting the job is not fully up to you, so you prepare to accept either outcome without a tantrum.

What is up to you includes preparing seriously, aiming for rest, and aiming to arrive on time, while recognizing that sleep and punctuality can still be disrupted by factors like noise, transit failures, or traffic.

This leads to a practical rule: own your intentions and preparation, and do not treat outcomes as guaranteed entitlements.

The same approach scales beyond personal life to social and political concerns that feel overwhelming.

He points to turbulence in ancient times and parallels it with current fears like climate change, nuclear war, and rapid political and social change.

With climate change, the individual cannot solve the global problem alone, but can still act through learning, donating, voting, persuading others, and joining public advocacy.

Those actions may not secure the desired result, but they make a real contribution and reduce helplessness because you are doing what is genuinely within your agency.

At the same time, you prepare emotionally for the possibility that outcomes still fail, because many results depend on collective systems beyond any one person.

Stoicism also addresses decision making, where people often second-guess themselves and force choices into black-and-white categories.

Epictetus centers this on prohairesis, the faculty of judgment and choice that assesses situations and commits to a course of action.

Prohairesis is your ability to judge a situation and choose how to respond.

The world is often complex, so trying to force it to fit your preferred simplicity is futile; it is better to understand how things actually work and act within that reality.

He illustrates this with a student complaining about a cold, where the Stoic response is not to demand the universe adjust, but to do the practical thing like using a handkerchief.

To improve judgment, Stoicism emphasizes the discipline of assent, meaning you learn to pause before endorsing the first impulse that comes with an experience.

Assent is saying "yes" to a thought, so you practice delaying that "yes" until you have examined it.

The Stoics say you are constantly hit by impressions, which combine what you perceive with an immediate, usually automatic evaluation.

An impression is a quick judgment that arrives вместе with what you notice.

A simple example is seeing tempting food and instantly concluding you should buy it, then stopping to ask whether it actually fits your goals and context.

This method shifts attention away from obsessing over outcomes and toward the reasons and values driving your choices.

For moral choices, the key question becomes whether your motivations express virtue, not whether the action looks good on the surface.

Volunteering can be virtuous if it comes from a genuine desire to help, but it can damage character if it is mainly a way to use others for status or resume benefits.

Stoicism frames the overall ethical aim as living according to nature, meaning understanding what kind of being a human is and what helps that being flourish.

Living according to nature means living in ways that help humans thrive as humans.

This makes ethics resemble an empirical discipline, because you must observe what actually supports human flourishing rather than inventing ideals detached from human life.

The Stoics liken ethics to medicine: some impulses are natural but harmful, like cravings for sugar and fat that once helped survival but now often undermine health.

They argue anger is similar for the psyche: natural as a reaction to perceived injustice, but typically damaging to a good life.

What supports mental health is reasoning through problems instead of reacting blindly, and building cooperative social relationships because humans flourish as social animals.

From there, concern should expand beyond the immediate circle to all people, since life is interconnected and cooperation scales.

Hierocles recommends imagining circles of concern that stretch from self to family, friends, acquaintances, and ultimately all humanity, then working to pull the outer circles closer.

He even suggests behavioral prompts, like addressing strangers with family-like terms, to remind yourself that everyone is part of the same human community.

Cosmopolitanism is treating every human as part of one shared community.

He returns to the earlier objection that these ideas are too simple by arguing that simplicity is not the obstacle; failure to practice is.

The Stoics compare ethical training to athletics: the movements can be easy to understand, but only repeated practice changes strength and capability.

He then explains his own path: he began as a scientist, then faced a midlife crisis about repeating the same work for decades.

A long-standing interest sparked by a philosophy teacher in Rome guided a shift into philosophy, including a move into philosophy of science.

The crisis deepened as several major stresses arrived close together, including a death in the family, an unexpected divorce, and relocating across the country.

He searched for a philosophy of life, explored Buddhism without finding the fit he hoped for, and narrowed toward virtue ethics in the Greco-Roman tradition.

While browsing X, he saw a mention of Stoic Week and became curious, despite earlier stereotypes of Stoics as emotion-suppressing like Mr. Spock from Star Trek.

Remembering earlier encounters with Marcus and Seneca, he tried the program and first read Epictetus, whose blunt style and emphasis on judgment immediately resonated.

A story about wanting wealth and power to help others leads to the Stoic point that money and power do not teach right action; improved judgment does.

He reports that starting to practice had quick effects, changing how he thought about persistent problems and how he behaved in response.

For weight, he applied the up-to-you distinction: genetics and early development set limits, but diet and exercise remain within agency, so he focused on the latter while accepting the former.

For anger, he learned to stop expecting people to behave ideally and to treat hurtful or foolish behavior as part of the world as it is, while still choosing thoughtful responses.

He stresses that this realism is not passivity or being a doormat; action matters, but it should be guided by reflection, aimed at what can be influenced, and free of fantasies about miracles.

2026-02-06 Do It With Style: Rethinking CSS - Dylan Beattie - NDC London 2026 - YouTube { www.youtube.com }

image-20260206002152479

image-20260206002240609In this talk we will discuss why CSS has such a bad reputation, why those complaints are often valid, and why it still matters because you cannot really build for the web without touching it.

We will then discuss why it is worth revisiting CSS today, because HTML and CSS are living standards and modern browser update cycles mean new capabilities become usable much faster than they used to.

We will discuss how features that used to require JavaScript or heavy libraries can increasingly be handled by native elements and modern CSS, using accordions as a concrete example with details/summary, better selectors, and cleaner authoring patterns like nesting.

We will discuss modern motion in CSS, including transitions, keyframe animation, and the long-running "transition to auto" problem, along with the opt-in fixes and the use of feature queries to progressively enhance without breaking older browsers.

We will discuss modern color in CSS, moving from named colors and RGB toward relative colors and perceptual color spaces like OKLCH, and how that enables practical theming and palette generation from a single user-chosen color.

We will discuss how gradients, layering, and blend modes let CSS generate complex visuals and even transform images, such as desaturating and tinting a background image, without needing JavaScript or external image processing.

We will conclude by discussing the evolving work on styling native form controls, especially select elements, and why enhancing built-in controls is usually better than replacing them, because you keep accessibility, keyboard support, and long-term maintainability.

2026-02-01 The history of C# and TypeScript with Anders Hejlsberg | GitHub - YouTube { www.youtube.com }

image-20260201111738238 image-20260201112803270


Anders Hejlsberg is one of the most influential language designers of our time, having created Turbo Pascal, Delphi, C#, and TypeScript. In this interview, he discusses his 40-year career, the early days of open source at Microsoft, and the decision to move TypeScript to GitHub. He also reveals why the team recently decided to port the TypeScript compiler to Go for a 10x performance boost.


Early access to a school computer and the arrival of 8-bit machines pushed him from curiosity into building his own kit computer, then writing enough software to realize he was good at it and enjoyed the craft.

Working with tiny memory limits forced a different mindset: keep designs small enough to fit, leave space for users, and rely on understanding the whole system well enough to hold it in your head.

Turbo Pascal aimed at the same instant feedback people liked in BASIC, but without paying the speed penalty of an interpreter and without settling for a crude line-based editing experience.

A "feedback loop" is the time between changing code and seeing the result.

The key was to make "Run" feel immediate by compiling straight into memory, avoiding disk writes, executing right away, and dropping back into the editor when errors appeared.

Slow compiles are universally frustrating because the moment you finish writing code is the moment you want to see it run, not the moment you want to start waiting.

Pricing turned out to be a strategic lever: selling far cheaper felt risky at first, but it enabled vastly higher volume, which ultimately made the product far more successful.

Growth forced a shift from a one-person approach to a team approach, because hardware capacity and user expectations rose faster than any single developer could scale.

Becoming a real team player meant accepting that work will be done differently than you would do it, and that other people's code will not match your personal style.

It also meant learning that "fixing" code you dislike often does not change product behavior enough to justify the time, especially when the schedule is tight.

Handing off responsibility is not optional at scale: people need clear ownership so they feel empowered to make decisions and deliver the parts they own.

"Empowerment" is giving someone real authority to make choices for the work they own.

At Microsoft, early Java tooling was assembled quickly but lacked deep integration and rapid iteration, so the goal became a more cohesive development experience rather than a set of loosely connected parts.

A pragmatic view guided the work: if you build for a specific platform, you should interoperate well with that platform instead of pretending every environment is identical.

Java's "write once, run everywhere" ideal often led to least-common-denominator UI and restrictions that blocked taking advantage of what a platform could do best.

A "least common denominator" design is one that stays generic by avoiding features that only some platforms support.

The core lesson was to design holistically, because users judge the combined experience of language, runtime, libraries, editor, debugger, and tooling rather than separating them into categories.

Avoiding organizational silos improves outcomes, since mismatched visions across components tend to produce awkward layers and compromises instead of a clean, best-of-breed system.

As browsers, JavaScript engines, and HTML matured, the industry shifted toward the browser as the real platform, which enabled much larger applications across many device types.

That growth exposed a painful reality: building big systems in a dynamic language without strong tooling leads to fragile code and organizational strain.

ScriptSharp was a warning sign: teams were willing to write in one language and compile to JavaScript just to get type checking, interfaces, and serious tooling for large-scale collaboration.

This sparked the idea that treating JavaScript as a compilation target was the wrong fix, and that improving the ecosystem would mean keeping JavaScript while addressing what breaks at scale.

Making the new language a strict superset reduced friction by meeting developers where they already were, so existing code stayed valid while optional types and better tooling improved reliability.

He is skeptical of "new language" ambition because most of the work is repetitive infrastructure, and modern expectations include broad IDE integration, debugging, profiling, and now AI-facing services.

The adoption curve is also brutal: meaningful usage can take many years, and a new language must survive long enough to earn trust and community investment.

Launching the project as open source was necessary to engage the JavaScript community, but early efforts were constrained by limited trust and by processes that still treated the public repo as a one-way drop.

Moving to GitHub and adopting a truly open workflow changed the trajectory: development happened through public pull requests, issues stayed public, and internal contributors operated under the same visible process as outsiders.

Open development benefits users by preserving the reasoning behind decisions in public threads, and it benefits maintainers by turning shipping into an ongoing dialogue instead of rare, high-stakes releases.

Community voting through issue upvotes provides a practical prioritization signal, creating a feedback cycle where shipping what users want increases trust and participation.

Porting the compiler to Go was driven by scale: JavaScript is single-threaded by design, lacks shared-memory concurrency, and leaves most available compute power unused on modern machines.

Even with deep expertise in JavaScript performance tricks, the team hit a ceiling, so native code plus concurrency offered a step-change, on the order of 10x improvements that could not be ignored.

The strategy was to port, not rewrite, because the type checker encodes many subtle behaviors that are not fully documented anywhere but the existing implementation.

Choosing the target language required matching the compiler's needs: cyclic data structures and garbage collection are central, making Rust impractical without turning the effort into a redesign.

Go fit well enough to produce a native compiler that behaves like a carbon copy of the old one, quirks included, which avoids forcing the ecosystem to relearn or discard existing assumptions.

AI-assisted coding changes incentives: the best target languages for AI are the ones with the most training data, so common languages gain an advantage while brand-new languages start behind.

Using AI to translate a large codebase is risky when you need deterministic equivalence, because small hallucinations force painstaking review, erasing the value of automation.

A better pattern is to ask AI to generate tooling that performs deterministic transformations, so the output can be trusted by rerunning the tool instead of auditing every line.

As AI improves at code fixes and refactors, some parts of traditional language services may matter less to port exactly, because the role of tooling shifts toward enabling AI workflows rather than reproducing old editor-era behavior.

A "language service" is the component that powers completions, navigation, refactors, and quick fixes in editors.

AI is most valuable when it removes toil: repetitive housekeeping, triaging large issue backlogs, checking whether old reports still reproduce, and migrating changes across divergent branches.

This raises a workforce concern: if AI replaces too much entry-level work, teams must find new ways to develop beginners into experts rather than narrowing the pipeline until it breaks.

Over the next 5 to 10 years, the language itself will largely track JavaScript standardization plus additional type-system features layered on top, while tooling direction is far less predictable.

The tooling shift is already visible: AI is moving from an assistant inside the IDE to an agent doing work under supervision, which may reduce the centrality of the traditional IDE while increasing reliance on semantic services.

Connecting semantic tooling to AI through MCP is a way to give agents structured, deterministic access to language understanding and refactoring capabilities.

"MCP" is a way for tools to provide structured capabilities that an AI can call.

Open source remains a balancing act because it is given away while being funded by organizations that must justify costs, creating ongoing tension about sustainability and incentives.

Many enterprises depend on open source maintenance, yet often contribute less than they consume, so the ecosystem still lacks a widely accepted model that reliably rewards maintainers.

He respects any language that achieves real adoption because getting there is hard, and he highlights Rust for its memory-management approach, Go for a simple type-safe, memory-safe systems style, and Python for its enormous impact.

As a designer, he views languages as cumulative learning: borrowing proven ideas is rational, and refusing to learn from predecessors is a mistake.

He remains optimistic about collaborative platforms because the work persists: years of searchable history capture decisions and evolution in a way that does not vanish into private email, even if the ecosystem also contains noise and abandoned projects.

2026-01-09 If Youre Ambitious but Lazy, Watch This Samurai Lesson (Kaizen Method to Success) - YouTube { www.youtube.com }

image-20260108212635244

I can talk myself into feeling like I am progressing, just by describing what I will do and picturing the future version of me who already succeeded. That mental rehearsal can become a reward that replaces real work.

When I build my identity around being a person with big plans, I create a gap between what I want and what I am willing to do today. I stay comfortable in the role of the dreamer instead of becoming the person who practices.

I learn that wanting something badly does not automatically make me act. Clear goals and strong ambition do not cross the distance between desire and daily effort.

The bigger the goal, the easier it is for me to feel overwhelmed by how far away it is. That feeling makes me delay the small steps that would actually bring it closer.

I can mistake preparation for progress. Planning, researching, and talking can feel productive while I quietly drift away from the only thing that changes me, which is practice.

If I keep speaking about my plans without acting, my mind treats it like an achievement. That makes inaction easier to repeat and makes regret more likely later.

Time is the hidden cost. Dreams without action spend it slowly, and once it is gone I cannot recover it.

I stop waiting for a burst of inspiration because that is just another way to postpone. If I wait for perfect conditions, I can wait forever while someone else moves forward in imperfect weather.

Motivation is not what I should depend on to start. More often, it shows up after I begin, because action creates momentum and momentum creates energy.

Motivation is the push I often feel after I start acting, not the feeling I must have before I begin.

I take the idea of small improvements seriously, because it turns change into something I can do daily. I climb by steps, not by staring at the peak and wishing.

Kaizen is improving through very small steps repeated over time.

Small steps work because they are repeatable. Even if the effort looks tiny, repetition makes it powerful, and consistency is what turns slow progress into real mastery.

If I practice even one small form each day, the movement becomes automatic. My body strengthens, my technique sharpens, and my discipline deepens, while the people who keep waiting stay at the starting line.

I stop trusting the illusion that talking equals doing. Talking without practice gives me the feeling of motion without the reality of change.

When I say I need motivation, I am often protecting myself from discomfort. I fear starting when I am tired, continuing when I feel resistance, and working without immediate reward.

Discipline is not the absence of those feelings. It is moving through them.

True strength is not acting only when conditions are perfect. It is acting when conditions are not.

I break laziness in a simple way: I begin with small daily actions even when they feel insignificant. I do not wait to feel ready, because readiness is a story my mind uses to delay.

I choose habits and commitments over emotion. Feelings change, but commitments can stay steady, and they can carry me forward when inspiration is missing.

Commitment is the decision I keep following even when my feelings change.

Strength is not saying I feel like doing this. Strength is saying I will do this whether I feel like it or not.

2026-01-08 Why Even The Best Engineers Are Afraid Of Whats Coming | Philip Su - YouTube { www.youtube.com }

image-20260107230325569

Philip Su is a software engineer and founder of the podcast app Superphonic (sometimes discussed alongside earlier naming like "Superic" in interviews). He previously held senior engineering roles at Microsoft, Meta, and OpenAI, and is often described as having reached Meta's Distinguished Engineer level (IC9). (LinkedIn)

He also writes about technology and society in his newsletter Molochinations and hosts the podcast Peak Salvation, which centers on his experience leaving tech leadership to work in an Amazon warehouse during Peak season and reflect on automation and the future of work. (molochinations.substack.com)

They shift from technical change to social impact, emphasizing that prior labor transitions happened across generations, while current automation pressures people already deep into a career to retrain under time stress.

A concrete example is professional voice acting, where he tells a friend that near term replacement is plausible and emotionally devastating because the work is specialized, identity forming, and often loved.

Philip Su distinguishes jobs he is happy to see automated, like physically punishing warehouse lifting, from creative or expressive work where automation can remove something people value doing.

His main near term worry is social instability, because displacement can happen faster than legal, regulatory, and institutional systems can adapt.

He says the usual historical argument, "we have always predicted job loss and been wrong," may fail because general purpose systems could outperform many people across many tasks, leaving fewer obvious fields to retrain into.

He expects many companies to choose "more with less" rather than "more with more," because human coordination overhead means adding people does not scale output linearly.

The mythical man-month is the idea that adding people to a project often adds coordination cost and does not scale output proportionally.

From that perspective, shrinking headcount while augmenting remaining staff with automation can reduce meetings and friction and increase per employee productivity, making layoffs economically attractive.

He predicts that developers who do not learn to use these tools risk being displaced by peers who do, even if the tools are imperfect today.

They separate commercial productivity from craft enjoyment: using AI can turn the job into reviewing mediocre generated code rather than writing code directly, which can reduce the joy for people who love the craft.

He compares this to whittling, where the point is not efficiency but the pleasure of making, and argues that some people will still choose hands on creation even if machines can do it better.

2025-12-27 Bad Estimates Destroy Careers (Heres What to Say Instead) - YouTube { www.youtube.com }

image-20251227003553680

image-20251227003628643When someone asks for a timeline before you understand the work, giving a confident answer is risky because it turns into a promise you cannot reliably keep.

Saying "two weeks" is often a reflex under pressure, but it is effectively a lie if it is not based on real understanding, and the cost of that lie shows up later when the project slips and you are blamed.

A single date assumes ideal conditions, but real projects include unknowns, legacy complexity, and unexpected problems, so early estimates are naturally uncertain.

People asking for deadlines are usually anxious rather than malicious; they want a number because it feels like a plan, even if the number has no foundation.

You should not give a single deadline; you should give a range that reflects uncertainty, such as "3 to 6 weeks", and narrow that range as you learn more.

An estimate range is a timeline expressed as earliest and latest likely completion, rather than one fixed day.

If someone wants a precise answer, you should require precise inputs, because vague requirements cannot produce precise timelines.

If pushed, you can ask for a short research period, such as two days, to investigate and then provide a range you actually believe.

A useful move is to reframe the request by asking what outcome they are trying to achieve, because solving the underlying goal can be better than building a complex implementation that does not match the real need.

When a range is rejected, you can transfer risk explicitly by explaining that a single date will likely be wrong and that it only holds if nothing goes wrong.

You can also propose a discovery step by committing to a brief investigation and a specific day when you will deliver a credible range.

Another option is to refuse the unrealistic date while offering a path forward, such as maintaining quality by extending time, finishing current commitments first, or adding another developer to hit the desired schedule.

Accepting fantasy timelines harms the business, not just the engineer, because bad estimates drive overruns, force weekends, and incentivize corner-cutting.

Under high pressure, quality drops and defects multiply, so an aggressive date does not truly save time; it creates future cost by accumulating technical debt and rework.

Technical debt is extra future work created when you take shortcuts now.

Your job is not to comfort others with a fake date; it is to provide truthful information so they can make tradeoffs and decisions.

Taking on impossible timelines leads to burnout and long-term dissatisfaction, so protecting estimate integrity is also protecting your ability to do sustainable work.

The recommended defense is to give a range, push for the missing information needed to narrow it, and reframe the problem toward the desired outcome.

A practical script is to acknowledge the request for a timeline, state the current range, and explain that tightening it requires more information, which positions you as professional rather than obstructive.

Video Comments:

gojkogalonja I'd rephrase 3-6 weeks into "It could take up to 6 weeks".

@dynabeen2 Here is the thing though - say 3 to 6 weeks and all they hear is 3 weeks.

· 11 min read

🌈Recreational Programming

2025-11-27 Crafting Interpreters { craftinginterpreters.com }

image-20251126221213720 Crafting Interpreters contains everything you need to implement a full-featured, efficient scripting language. You’ll learn both high-level concepts around parsing and semantics and gritty details like bytecode representation and garbage collection. Your brain will light up with new ideas, and your hands will get dirty and calloused. It’s a blast.

Starting from main(), you build a language that features rich syntax, dynamic typing, garbage collection, lexical scope, first-class functions, closures, classes, and inheritance. All packed into a few thousand lines of clean, fast code that you thoroughly understand because you write each one yourself.

it is also available for free

image-20251126221418572

2025-11-27 My 2 Year Journey of Learning C, in 9 minutes - YouTube { www.youtube.com }

image-20251126221535812


This is a short video about my journey from not understanding C in the least to being able to make a relatively large codebase.

2025-11-27 PixelRifts/c-codebase: A simple base layer, and utilities for my own C development. { github.com }

A lot of the stuff came from https://www.youtube.com/c/Mr4thProgramming, but I have made a few simplifications/modifications

🔫 C || C++

2025-12-27 floooh/sokol: minimal cross-platform standalone C headers { github.com }

image-20251227140116254

Examples and Related Projects

Core libraries

  • sokol_gfx.h: 3D-API wrapper (GL/GLES3/WebGL2 + Metal + D3D11 + WebGPU)
  • sokol_app.h: app framework wrapper (entry + window + 3D-context + input)
  • sokol_time.h: time measurement
  • sokol_audio.h: minimal buffer-streaming audio playback
  • sokol_fetch.h: asynchronous data streaming from HTTP and local filesystem
  • sokol_args.h: unified cmdline/URL arg parser for web and native apps
  • sokol_log.h: provides a standard logging callback for the other sokol headers

Utility libraries

image-20251227140319774

2025-12-27 Sokol WebGL { floooh.github.io }

image-20251227140404736

2025-12-27 floooh/chips: 8-bit chip and system emulators in standalone C headers { github.com }

A toolbox of 8-bit chip-emulators, helper code and complete embeddable system emulators in dependency-free C headers (a subset of C99 that compiles on gcc, clang and cl.exe).

Tests and example code is in a separate repo: https://github.com/floooh/chips-test

The example emulators, compiled to WebAssembly: https://floooh.github.io/tiny8bit/

For schematics, manuals and research material, see: https://github.com/floooh/emu-info

The USP of the chip emulators is that they communicate with the outside world through a 'pin bit mask': A 'tick' function takes an uint64_t as input where the bits represent the chip's in/out pins, the tick function inspects the pin bits, computes one tick, and returns a (potentially modified) pin bit mask.

A complete emulated computer then more or less just wires those chip emulators together just like on a breadboard.

In reality, most emulators are not quite as 'pure' (as this would affect performance too much or complicate the emulation): some chip emulators have a small number of callback functions and the adress decoding in the system emulators often take shortcuts instead of simulating the actual address decoding chips (with one exception: the lc80 emulator).

image-20251227141009312

2025-12-27 Home | rxi { rxi.github.io }

image-20251226232528824

A Simple UI Animation System A minimal tweening system for immediate mode UIs that tracks only active animations in a fixed array, exposes an update/start/get API, and lets your program store only target values while rendering uses animated values.

A Simple Serialization System A self-describing binary serialization format built from tagged values (including arrays and objects), designed for simple implementation, linear reading, easy inspection, and forward/backward compatibility, with an optional string interning extension.

Textbox Behaviour A reference specification for textbox editing behavior that breaks the problem into caret/selection state plus movements, operations, and commands, covering single-line, multi-line, and mouse-driven selection details.

Level Generation Using Markov Chains A method for generating 2D tilemap levels by training a Markov chain on one-dimensional row strings from example maps, then generating new rows and optionally placing entities by scanning for special tiles.

A Simple Undo System An undo/redo approach where code marks memory blocks before they might change and commits at interaction end to diff and record only changed blocks, implemented with undo, redo, and temporary stacks.

lite: An Implementation Overview An implementation tour of the lite text editor, describing its Lua/C split, frame loop, cooperative coroutine-based background work, document management, incremental syntax highlighting, view-based UI layout, and plugin approach.

microui v2: An Implementation Overview An implementation overview of a tiny immediate mode UI library in ANSI C that turns input into draw commands within fixed buffers, explaining windows and controls, hover rules, z-ordering in one command list, and bounded state storage.

Cached Software Rendering A software rendering technique that lets the app redraw as if every frame is full while the renderer redraws only changed regions using a command buffer plus a per-cell hash grid to detect dirty areas.

2025-11-27 A Very Fast 64–Bit Date Algorithm: 30-40% faster { www.benjoffe.com }

In this article I present my final very fast date conversion algorithm. It represents a significant speed gain — being similar in magnitude to the speed gains achieved by the previous fastest algorithm (Neri-Schneider 2021) over its predecessor (C++ Boost). The full algorithm implementation in C++ is released as free open source software (BSL-1.0 License).

The algorithm provides accurate results over a period of ±1.89 Trillion years, making it suitable to process the full UNIX 64–bit time (in seconds).

The entire algorithm has been re-written top-to-bottom, with various micro-optimisations, but three main new ideas:

  • Years are calculated *backwards*, which removes various intermediate steps.
  • The step to calculate day-of-year is *skipped*, instead using a year-modulus-bitshift technique which removes a division.
  • The *"Julian Map"* technique is utilised from my previous article, which speeds up the 100/400 year calculation, removing two more hardware multiplications.

While fast date algorithms have always used 7 or more expensive computations (multiplication, division, or modulus by non-power-of-2 numbers), this algorithm uses only 4 multiplications. The speed-gain can be seen at a glance. image-20251126202337453

💖 Inspiration!

2026-01-17 Compilation: Ask HN: Share Your Personal Website { blog.zharii.com }

Compilation of Ask HN: Share your personal website | Hacker News { news.ycombinator.com }

image-20260117134446397

2026-01-17 DustinBrett/daedalOS: Desktop environment in the browser { github.com }

image-20260117133808968

2026-01-17 Ganbaru Games | Browser-based games, puzzle and otherwise. { ganbaru.games }

image-20260117124453281

2026-01-17 Generative Storytelling - Exploring Storytelling with AI and LLMs { www.generativestorytelling.ai }

image-20260117124327311

2026-01-17 Making Sense of Lambda Calculus 6: Recurring Problems { aartaka.me }

image-20260117123705689

2026-01-17 Michael Ongaro { www.michaelongaro.com }

image-20260117123419729

2026-01-17 Carpe Diem (Aug 24, 2024) - TinyMCE - daedalOS { dustinbrett.com }

image-20260117002226484

2026-01-17 Nick Smith - Senior Software Engineer { nicksmith.software }

image-20260117002518108

2026-01-17 Window management { nabraj.com }

2026-01-17 projects | nabraj.com { nabraj.com }

image-20260117002836931

2026-01-05 ELIZA { anthay.github.io }

anthay.github.io/eliza.html at main · anthay/anthay.github.io

Joseph Weizenbaum’s 1966 ELIZA recreated in C++

anthay/ELIZA: A Simulation in C++ of Joseph Weizenbaum’s 1966 ELIZA

I’ve made in C++ what I think is an accurate simulation of the original ELIZA. It is a console application that takes as input the original format script file, which looks like a series of S-expressions, and then waits for the user to type a line of text before responding with a line of text of its own.

image-20260105151842153

2025-12-25 junegunn/fzf: 🌸 A command-line fuzzy finder { github.com }

image-20251225000101756

image-20251225000151137

It's an interactive filter program for any kind of list; files, command history, processes, hostnames, bookmarks, git commits, etc. It implements a "fuzzy" matching algorithm, so you can quickly type in patterns with omitted characters and still get the results you want.

2025-12-24 antonmedv/textarea: A notes webapp { github.com }

image-20251224144643967

2025-12-11 Patterns.dev { www.patterns.dev }

image-20251210200041075

image-20251210200103086

2025-12-10 A series of tricks and techniques I learned doing tiny GLSL demos { blog.pkh.me }

image-20251209182400139

2025-12-09 fanfa.dev - Animated, interactive And visuals Mermaid Diagrams { fanfa.dev }

image-20251208220817420

2025-12-02 rothgar/awesome-tuis: List of projects that provide terminal user interfaces { github.com }

image-20251201220908642

2025-12-02 joouha/euporie: Jupyter notebooks in the terminal { github.com }

Euporie is a terminal based interactive computing environment for Jupyter.

Euporie's apps allow you to interact with Jupyter kernels, and run Jupyter notebooks - entirely from the terminal.

If you're working with Jupyter notebooks in a terminal only environment, like an SSH server or a container, or just prefer working in the terminal, then euporie is the tool for you!

image-20251201225515432

2025-11-29 Be Like Clippy { be-clippy.com }

image-20251129155807871

2025-11-27 penpot/penpot: Penpot: The open-source design tool for design and code collaboration { github.com }

Penpot is the first open-source design tool for design and code collaboration. Designers can create stunning designs, interactive prototypes, design systems at scale, while developers enjoy ready-to-use code and make their workflow easy and fast. And all of this with no handoff drama.

Available on browser or self-hosted, Penpot works with open standards like SVG, CSS, HTML and JSON, and it’s free!

image-20251127003847879 image-20251127003940066

We have very precise rules on how our git commit messages must be formatted.

The commit message format is:

[type] [subject]

[body]

[footer]

Where type is:

  • 🐛 :bug: a commit that fixes a bug
  • :sparkles: a commit that adds an improvement
  • 🎉 :tada: a commit with a new feature
  • ♻️ :recycle: a commit that introduces a refactor
  • 💄 :lipstick: a commit with cosmetic changes
  • 🚑 :ambulance: a commit that fixes a critical bug
  • 📚 :books: a commit that improves or adds documentation
  • 🚧 :construction: a WIP commit
  • 💥 :boom: a commit with breaking changes
  • 🔧 :wrench: a commit for config updates
  • :zap: a commit with performance improvements
  • 🐳 :whale: a commit for Docker-related stuff
  • 📎 :paperclip: a commit with other non-relevant changes
  • ⬆️ :arrow_up: a commit with dependency updates
  • ⬇️ :arrow_down: a commit with dependency downgrades
  • 🔥 :fire: a commit that removes files or code
  • 🌐 :globe_with_meridians: a commit that adds or updates translations

More info:

· 77 min read

image-20260110002236729

CRDT

🏵️ 2025-11-29 The CRDT Dictionary: A Field Guide to Conflict-Free Replicated Data Types - Ian Duncan - Ian Duncan { www.iankduncan.com }

image-20251129110810492 image-20251129112659154


Explains how to design and use conflict free replicated data types to handle concurrent updates without coordination, walking through the core idea of lattices and monotone joins, state based vs operation based variants, and concrete structures for counters, sets, registers, maps, and sequences. Shows how different semantics arise (grow only, two phase, last write wins, add wins, multi value) and how to compose these pieces into practical data structures like shopping carts, collaborative text, and replicated maps, including causal and delta based optimizations.

Digs into the real tradeoffs: metadata growth, tombstones, garbage collection, causal tracking, bandwidth, and the need for supporting protocols like causal broadcast. Stresses that nothing is free; each structure trades coordination for more state and weaker semantics, so the right choice depends on operations needed, tolerance for lost updates, and operational constraints, with a strong push to treat CRDTs as a targeted tool to be combined and tuned rather than a default magic solution.


  • G-Counter: Grow-only counter where each replica keeps its own count and merge takes per-replica max; use for monotonic metrics like page views, likes, or any count that only increases.
  • PN-Counter: Counter built from two G-Counters (increments and decrements) whose values are subtracted; use for inventory, resource pools, or any count that must go up and down.
  • G-Set: Grow-only set that supports add and merge=union but no removals; use for append-only collections like tag registries, logs of seen items, or immutable membership.
  • 2P-Set: Two-phase set with separate grow-only add and remove sets where removal is permanent; use when elements can be created then permanently retired but never re-added (e.g., tombstones, revoked IDs).
  • LWW-Element-Set: Set that tracks per-element add/remove timestamps and lets the latest operation win; use when you need add/remove/re-add and can tolerate last-write-wins data loss (preferences, feature flags, cached sets).
  • OR-Set: Observed-remove set that tracks per-element tags so removes only delete observed additions, giving add-wins semantics; use when concurrent adds must never be lost (collaborative lists, shopping carts, shared sets).
  • LWW-Register: Single-value cell with a timestamped value where the latest timestamp wins; use for fields where occasional lost concurrent updates are acceptable (profile fields, cached config).
  • MV-Register: Multi-value register that stores all concurrent writes instead of discarding them; use when you must detect and resolve conflicts in application logic (collaborative text fields, conflict-aware configs).
  • Causal Register: Register keyed by version vectors that keeps only values with concurrent causal histories; use when you want MV-Register behavior plus precise causal conflict detection and better GC.
  • OR-Map: Map whose keys and/or values are backed by OR-Set semantics, often with nested CRDTs per value; use for replicated JSON-like documents, distributed configuration maps, and nested structures.
  • RGA (Replicated Growable Array): Sequence where elements have immutable IDs and parent links, supporting inserts-after and tombstoned deletes; use for collaborative text or lists where arbitrary-position inserts must merge cleanly.
  • WOOT: Sequence CRDT representing characters as objects with prev/next links and visibility flags, resolving order via constraints; use mainly as a historical or academic model, not typically in new production systems.
  • Logoot: Sequence CRDT assigning each element a dense ordered position identifier; use for collaborative sequences when you prefer position-based ordering over pointer-based structures.
  • LSEQ: Variant of Logoot with adaptive position allocation to keep identifiers shorter; use as a practical improvement over plain Logoot when identifier growth is a concern.
  • Tree CRDTs: Family of structures for replicated trees that preserve parent-child relationships under concurrency; use when you truly need CRDT-level guarantees over hierarchical data like file trees or document outlines.
  • OR-Tree: Tree CRDT that stores an OR-Set of parents per node and resolves parent conflicts with policies like LWW or first-wins; use for replicated hierarchies where concurrent moves must be reconciled automatically.
  • CRDT-Tree: Tree design that relies on causal ordering of move operations to pick winners; use when you already enforce causal delivery and want deterministic, causality-driven resolution of structural conflicts.
  • Log-based Trees: Tree approach that logs operations and rebuilds structure on read from a replicated log; use when reads can afford reconstruction cost and you want simple, append-only operational histories.
  • Delta CRDTs: Any state-based CRDT extended with a delta mechanism that sends only changes instead of full state; use whenever state is large or bandwidth is a concern, especially in production systems.
  • Causal CRDTs (e.g., Causal OR-Set, causal maps): CRDTs augmented with version vectors or similar clocks to track happens-before and prune dominated history; use when you need precise conflict classification and safer garbage collection.
  • Causal OR-Set: OR-Set variant that attaches version vectors to tags and uses them to decide what metadata can be safely discarded; use for long-lived sets where tag GC matters and causal tracking is already in place.
  • CheckpointedCRDT: Wrapper pattern that periodically compacts history into a baseline snapshot plus recent deltas; use when most replicas are online often and you want aggressive pruning at the cost of occasional full resyncs.
  • Observed-Remove Shopping Cart (OR-Set + PN-Counter): Composite CRDT mapping products to PN-Counters under OR-Set semantics; use for offline-capable carts where concurrent adds/removes and quantity changes must merge without data loss.

  1. The term “Conflict-free Replicated Data Type” was coined by Marc Shapiro, Nuno Preguiça, Carlos Baquero, and Marek Zawirski in their 2011 paper “Conflict-free Replicated Data Types” (technical report) and the 2011 SSS conference paper “A comprehensive study of Convergent and Commutative Replicated Data Types”. The theoretical foundations draw from earlier work on commutative replicated data types and optimistic replication.
  2. WOOT was introduced by Oster, Urso, Molli, and Imine in “Data Consistency for P2P Collaborative Editing” (2006). The name is a play on “OT” (Operational Transformation), emphasizing that it achieves similar goals “WithOut OT.” WOOT was one of the first practical sequence CRDTs and influenced many subsequent designs.
  3. State-based CRDTs are also called “convergent” replicated data types (CvRDT). The “Cv” stands for “convergent” - emphasizing that replicas converge to the same state by repeatedly applying the join operation.
  4. Operation-based CRDTs are also called “commutative” replicated data types (CmRDT). They require causal delivery of operations - if operation A happened before operation B on the same replica, B must not be delivered before A at any other replica.
  5. The G-Counter appears in Shapiro et al.’s 2011 technical report “A Comprehensive Study of Convergent and Commutative Replicated Data Types” as one of the foundational examples demonstrating CRDT principles.
  6. The space complexity is O(n) where n is the number of replicas, not the number of increments. This means G-Counters scale well with the number of operations but require tracking all replicas that have ever incremented the counter.
  7. The OR-Set (Observed-Remove Set) was introduced by Shapiro et al. in their 2011 technical report. It’s also known as the “Add-Wins Set” because concurrent add and remove operations result in the element remaining in the set. The key innovation is using unique tags to distinguish between different additions of the same element.
  8. Sequence CRDTs are particularly challenging because positional indices change as elements are inserted or deleted. Unlike sets or counters where elements have stable identity, sequences must maintain ordering despite concurrent modifications at arbitrary positions.
  9. RGA was introduced by Roh et al. in “Replicated Abstract Data Types: Building Blocks for Collaborative Applications” (2011). The name “Replicated Growable Array” emphasizes that it’s an array-like structure that can grow through replication.
  10. YATA (Yet Another Transformation Approach) was developed by Kevin Jahns for the Yjs collaborative editing library. It combines ideas from RGA and WOOT while optimizing for the common case of sequential insertions (typing). Yjs is used in production by companies like Braid, Row Zero, and others for real-time collaboration.
  11. Version vectors were introduced by Parker et al. in “Detection of Mutual Inconsistency in Distributed Systems” (1983). They extend Lamport’s logical clocks to track causality in distributed systems. Each replica maintains a vector of logical clocks (one for each replica), enabling precise causal ordering without requiring synchronized physical clocks.
  12. Delta CRDTs were introduced by Almeida, Shoker, and Baquero in “Delta State Replicated Data Types” (2018). They bridge the gap between state-based and operation-based CRDTs, achieving operation-based bandwidth efficiency while maintaining state-based simplicity. Most production CRDT systems (Riak, Automerge) use delta-state internally.
  13. Logoot was introduced by Weiss, Urso, and Molli in “Logoot: A Scalable Optimistic Replication Algorithm for Collaborative Editing” (2009). The name combines “log” (logarithmic complexity) with “oot” from WOOT, its predecessor. Logoot’s position-based approach influenced many subsequent CRDTs including LSEQ and Treedoc.
  14. LSEQ was introduced by Nédelec, Molli, Mostéfaoui, and Desmontils in “LSEQ: An Adaptive Structure for Sequences in Distributed Collaborative Editing” (2013). The key innovation is using different allocation strategies (boundary+ vs boundary-) based on tree depth, which keeps position identifiers shorter in practice compared to Logoot’s fixed strategy.
  15. Automerge, created by Martin Kleppmann and collaborators, implements a JSON CRDT described in “A Conflict-Free Replicated JSON Datatype” (2017). It uses a columnar encoding for efficiency and has been rewritten in Rust for performance. Used by production apps like Inkandswitch’s Pushpin.
  16. Yjs, created by Kevin Jahns, is optimized for text editing and uses the YATA algorithm. It’s notably faster than Automerge for text operations and includes bindings for popular editors like CodeMirror, Monaco, Quill, and ProseMirror.
  17. Riak, a distributed database from Basho, was one of the first production systems to adopt CRDTs (2012). It implements counters, sets, and maps as native data types, using Delta CRDTs internally to minimize bandwidth. Sadly, the company collapsed dramatically, and the project was abandoned for quite some time. I think it’s still around in a diminished form, but haven’t tried it in a while.
  18. Redis Enterprise’s CRDT support (Active-Active deployment) uses operation-based CRDTs with causal consistency. It supports strings, hashes, sets, and sorted sets with CRDT semantics, enabling multi-master Redis deployments.
  19. AntidoteDB is a research database from the SyncFree project that makes CRDTs the primary abstraction. Unlike other databases where CRDTs are a feature, AntidoteDB is designed from the ground up around CRDT semantics, providing highly available transactions over CRDTs.

2025-12-06 Martin Kleppmann CRDTs: The hard parts - YouTube { www.youtube.com }

image-20251206120639388The talk introduces conflict free replicated data types as a way to build collaboration software where several people can edit shared state, such as documents, graphics, or task boards, even while offline, and then have all changes merged automatically without manual conflict resolution.

A conflict free replicated data type is a data structure that guarantees all replicas end up in the same state after exchanging all updates, without needing central coordination.

One established approach to collaborative text editing is operational transformation, where every change is recorded as an operation like insert or delete at a numeric index in the document, and when concurrent edits arrive, their positions are transformed so they still apply correctly to the modified document, a process that assumes all operations are totally ordered by a single server.

Operational transformation is a method where edits are indexed by position and later adjusted so they still make sense after other edits change the document.

A key limitation of that older family of algorithms is the reliance on a central server that sequences all edits, which prevents using peer to peer channels, local networks, or offline media to synchronize, because any side channel would break the single ordered stream of operations the method depends on.

The newer family of replicated data types solves the same general problem but avoids indexes and central ordering by giving each element in the document a unique identifier, allowing edits to commute regardless of network topology, and targeting a core correctness property called convergence: if two replicas have seen the same set of operations, they must be in the same state, no matter the order of delivery.

Convergence means that any two replicas that have processed the same updates, in any order, must show exactly the same data.

However, convergence alone is not sufficient, because it says only that everyone ends up in the same state, not that this state is meaningful or desirable for users; many simple designs converge to results that are technically consistent but clearly wrong or unusable from a human perspective, so additional constraints and better algorithms are needed.

One common design for text in these data types is to represent each character with a fractional position between 0 and 1 instead of an index, assigning numbers like 0.2, 0.4, 0.6, and 0.8 to successive characters, and when inserting between two positions, choosing any number between them, perhaps randomly, which allows new characters to be ordered without shifting indexes.

With that scheme, if two people independently insert different words at the same place, they both generate multiple positions in the same numeric interval between two existing characters, and when those sets of characters are merged and sorted by position, the letters from both words can become arbitrarily interleaved, producing output that is a jumble of mixed characters rather than two readable words in sequence.

This interleaving anomaly has been found in at least two specific list based algorithms in the literature, where the design of their position identifiers makes it impossible to prevent such mixing without completely changing the algorithm, while other schemes that do not suffer from this issue, like some tree based ones, are much less efficient and were partly the motivation for the problematic designs.

An interleaving anomaly is when concurrent inserts at the same place are merged in a way that mixes characters or chunks from different users, producing unreadable or surprising text.

Another widely discussed list algorithm uses a different structure: each inserted element remembers its predecessor at the time of insertion, forming a tree like structure based on cursors; this avoids arbitrary character level interleaving in typical use, but still allows block level interleaving where whole words or segments can end up woven between each other under some cursor movement patterns.

In this predecessor based structure, a user might type “dear reader” by first inserting “reader” and then moving the cursor back and inserting “dear”, while another user inserts “Alice” at the same place; depending on the order of operations, outcomes like “hello dear Alice reader” are allowed, which are sometimes acceptable but show that concurrent insertions can slip between earlier insert segments.

The worst theoretical behavior of that structure occurs if a user types the entire document backwards, constantly jumping the cursor to the front, which would allow arbitrary character interleaving, but under the realistic assumption that people mostly type forward with occasional cursor moves, the problem is much smaller, and this makes that algorithm more attractive than ones with inherent character scrambling.

The speaker and collaborators prefer this predecessor based approach over the more pathological schemes and have developed an extended version that eliminates even these less severe interleavings by refining the insertion rules; the details and proofs are in a separate research paper that formalizes the problem and presents a corrected variant.

The talk then turns to moving items in lists, as in a to do app where a user drags an item like “Phone Joe” to the top, and points out that existing list structures built for text only support insertion and deletion, so developers often simulate a move by deleting at the old position and inserting at the new one.

If two replicas simulate a move in this way and both perform the same move concurrently, each one deletes the old item once but inserts it at the new position twice, so when their changes are merged the list contains duplicated items, which is not what users expect when they drag a single entry.

To define more reasonable behavior, the speaker considers the case where two people move the same item to different places: instead of duplicating, a more useful semantics is that the item appears only once in the final list, at one of the requested positions chosen arbitrarily but deterministically, which mirrors how a last writer wins register handles conflicting updates.

A last writer wins register is a variable where concurrent writes are resolved by picking a single winner deterministically, usually based on timestamps or IDs.

Using this analogy, each list item can have an associated value that describes its position, stored in a last writer wins register, and when different replicas move the item, they simply assign different position values; when merged, the register chooses one winning position, so the item appears only once, and all replicas agree on where it ended up.

To implement this, the design reuses any existing list structure that already produces stable, unique position identifiers for arbitrary insertion points, and combines it with a set structure that holds items and their position registers, so moving an entry becomes “allocate a new position ID where you want it to appear, and update the item’s register to that ID,” giving a move operation that works with any underlying list algorithm.

This compositional construction yields a new list data type that supports atomic moves of single items with sensible semantics under concurrency, without modifying the underlying sequence structure, and shows how combining simple replicated components such as sets, registers, and list position IDs can express more complex operations.

The talk then examines moving ranges of text, such as moving a whole list item represented as a line of characters with a bullet and newline, and shows a counterexample where one replica moves the range “milk” above “bacon” while another edits “milk” into “soy milk”; the intuitive desired result is that the edited text appears in its new position.

Applying the single item move construction naïvely to each character or range causes edits to remain tied to the old positions, so the moved copy is “milk” while the edit turns the original location into a partially applied change like a stray “soy m” after “bacon”, revealing that properly moving segments while preserving concurrent edits is significantly harder than moving individual logical items.

The speaker notes that they do not yet have a fully satisfactory and safe general solution for moving ranges of characters with correct interaction with concurrent edits, and that this remains an open research question that others are invited to work on.

The next topic is moving nodes in tree shaped data structures such as file systems, JSON documents, or XML, where nodes represent directories or objects and operations can move an entire subtree from one parent to another, and the same concurrency issues appear when multiple replicas move the same node.

If one replica moves a node under B and another concurrently moves it under C, simple strategies include duplicating the subtree so it appears under both parents, or treating the structure as a general graph where a node can have multiple parents, but both are undesirable in many applications that expect a proper tree, so again the best option is to choose one destination as the winner and discard the other move.

Trees add a second challenge absent from linear lists: cycles, as illustrated by trying to move directory A into its own child B or more subtly by two replicas moving A under B and B under A concurrently, which can create a loop in the parent pointers and break the tree structure if not detected and prevented.

Real systems like file systems detect direct self moves and reject them as invalid, but concurrent cross moves from different replicas cannot be caught locally in the same simple way, and the talk describes experiments where cloud storage failed with vague errors under such patterns, motivating a rigorous algorithm that guarantees the tree remains acyclic.

To handle these moves, each operation is represented with a globally unique timestamp (for example a Lamport clock), the identifier of the node being moved, the new parent, and some metadata like a local name, and all operations on a replica are conceptually ordered by their timestamps so that the effect of concurrent moves can be judged in a consistent historical order.

Because operations may arrive out of timestamp order, the algorithm maintains a log that supports undo and redo: when an operation with an earlier timestamp arrives, the system temporarily undoes all later operations, applies the new one, and then reapplies the undone ones, so that, in effect, the tree has been modified as if all operations were processed strictly in timestamp order.

The cost of this backward and forward replay grows with the number of operations processed, but experiments with three replicas on different continents performing many moves show that even with this overhead, a simple implementation can handle on the order of hundreds of moves per second, which is sufficient for interactive applications where humans generate edits relatively slowly.

Within this framework, the algorithm defines an ancestor relation on nodes: a is an ancestor of b if it is the parent of b or the parent of some ancestor of b; before applying a move of child under parent, it checks whether child is already an ancestor of parent or identical to parent, and if so, it discards the move because it would introduce a cycle.

If the move passes this check, the operation removes the old parent child edge from the tree and inserts the new one with its metadata; the authors prove that this preserves the tree properties of unique parents and absence of cycles, and that for any set of moves, the final tree is the same on all replicas, so the structure is a valid replicated data type.

The final major topic is performance and space overhead, especially for text, where each character carries not only its byte of content but also a position identifier, an actor identifier, and additional metadata, so the per character overhead can easily be tens or hundreds of bytes, making naive implementations impractical.

The speaker reports on work in the AutoMerge project using a real dataset: the full editing history of an academic paper written in a custom editor that logged every keystroke and cursor move, producing a final LaTeX file of about 100 kilobytes and roughly 300,000 recorded changes including insertions, deletions, and cursor movements.

Storing this history as a simple JSON log of operations yields about 150 megabytes, which compresses to around 6 megabytes with gzip, but by redesigning the storage format they can encode the same full history in about 700 kilobytes, a roughly 200x improvement over the naive encoding, without losing any information about past edits.

They then explore further tradeoffs: discarding cursor movement events reduces the size by roughly a fifth, discarding full editing history while keeping only the data needed to merge the current state cuts it further down to a few hundred kilobytes, and if one also removes tombstones that track deleted characters, the metadata overhead shrinks to on the order of tens of kilobytes.

Tombstones in this context are markers that remember where deleted elements used to be so that concurrent edits can still be merged correctly.

One version of the compressed format, with history for text but not cursors and with merge relevant metadata retained, gzips to almost the same size as the raw LaTeX text, showing that with careful design, these data types can be implemented with overhead comparable to traditional version control while still supporting rich merging and offline edits.

The compression method keeps the idea of storing all operations with unique identifiers, often Lamport timestamps composed of a counter and an actor ID, and references predecessors (as in the predecessor based list algorithm) to specify where new characters are inserted, but organizes these operations into columns and encodes each column separately.

For a simple example, operations are tabulated with columns for timestamp counter, actor ID, predecessor reference, inserted text, length of the inserted UTF 8 sequence, and flags for deletion, then numeric columns are delta encoded so that successive values become small differences, run length encoded where repeated values occur, and finally written using variable length integer encoding that uses fewer bytes for small numbers.

The text column is compacted by concatenating the bytes of all inserted characters while lengths and deletion flags allow reconstructing which subsequences belong to which operations, so together with some modest metadata about event grouping and ranges of counters, the system can reconstruct the document at any past time while storing the entire operation log in a very compact binary representation.

The question period turns to delta based replicated data types, which combine characteristics of state based and operation based approaches by aggregating several adjacent changes into small deltas that can be merged idempotently, and the speaker notes that while this is natural for counters and sets, it is less useful for text and list structures where operations are insertions and deletions at specific places rather than arithmetic updates.

A delta based replicated structure sends compact summaries of recent changes instead of individual operations or full state, but still provides a merge function that can be applied repeatedly without changing the result.

Another question concerns snapshotting or garbage collecting the operation log used for undo and redo; the answer is that logs can be truncated safely once causal stability is reached, meaning all nodes are known to have applied all operations up to some timestamp, beyond which no older operations will arrive, but determining that point is hard in practice because a single offline node can delay the stability frontier.

Causal stability is the point in time up to which every replica has seen all updates, so older metadata can be safely discarded.

There is discussion about whether it still makes sense to use these replicated structures when a system already uses a single server for synchronization, and the speaker explains that historically operational transformation had an efficiency advantage for plain text in such settings, but the new metadata compression makes the newer approach competitive, while the latter also scales better to richer data types and multi data center server replication.

Comparing the two families, the speaker suggests that if a system only needs plain linear text and can rely on a robust single sequencer with well tested implementations, the older approach can be acceptable, but for applications that need trees, complex documents, or server side replication across data centers, the more recent data types provide a simpler correctness story and avoid the fragile single server requirement.

The final question asks about implementing modal editors like Vim on top of these structures, and the response is that most editor commands ultimately decompose into insertions, deletions, cuts, copies, and moves of ranges, which can in principle be expressed using the foundational operations discussed, though there is still open work on recognizing and coalescing sequences like cut then paste into semantic moves.

Throughout the answers, the speaker emphasizes that beyond formal convergence, the ultimate test for a merging strategy in editors is whether it matches human expectations in real use, and that much of the ongoing research is about refining the behavior of these replicated data types until they converge not just to a single state, but to one that users experience as natural and correct.

2025-11-30 dotJS 2019 - James Long - CRDTs for Mortals - YouTube { www.youtube.com }

image-20251130105613938

2025-11-30 jlongster/crdt-example-app: A full implementation of CRDTs using hybrid logical clocks and a demo app that uses it { github.com }

This is a demo app used for my dotJS 2019 talk "CRDTs for Mortals"

Slides here: https://jlongster.com/s/dotjs-crdt-slides.pdf

View this app here: https://crdt.jlongster.com

It contains a full implementation of hybrid logical clocks to generate timestamp for causal ordering of messages. Using these timestamps, CRDTs can be easily used to change local data that also syncs to multiple devices. This also contains an implementation of a merkle tree to check consistency of the data to make sure all clients are in sync.

It provides a server to store and retrieve messages, so that clients don't have to connect peer-to-peer.

The entire implementation is tiny, but provides a robust mechanism for writing distributed apps:

  • Server: 132 lines of JS
  • Client: 639 lines of JS

(This does not include main.js in the client which is the implementation of the app. This is just showing the tiny size of everything needed to build an app)

Links:


The talk starts from the question of why apps that work offline by design have not become common. The core claim is that making everything local - all code and data stored on the device - is straightforward, but the hard part is syncing that local state across devices without data loss or scary "changes may not be saved" errors.

The key step is to recognize that a local app used on multiple devices is a distributed system. Each device runs its own copy, can go offline, make changes, then later reconnect, and all those independent histories must be merged safely.

The speaker describes building a personal finance app that is fully local but syncs across devices. The design goals are instant offline availability, high speed, strong privacy, and the ability to run arbitrary queries, all of which naturally follow when all data lives on the device.

Because all data is local, the app can expose a query interface that directly compiles user input to SQLite, allowing custom reports and even query-like code from the user. This would be unsafe or unacceptable in a cloud environment, where arbitrary code on the server is a security and reliability risk, but is fine when it only touches the user's own local database.

The need for a mobile client to record transactions on the go forced the creation of a sync engine. The app's data is small - a few megabytes in SQLite - and the author refused to switch databases because SQLite's extremely fast reads are central to the user experience, so syncing had to be built as a thin layer on top of SQLite rather than as a replacement.

Syncing is described as hard because of two fundamental challenges: unreliable ordering of changes between devices, and conflicts when multiple devices edit the same data. The solution must run correctly 100 percent of the time, with no data loss and no irrecoverable states, because the app is local and cannot be "fixed" by refreshing a browser tab.

Unreliable ordering arises because different devices make changes in parallel and receive each other's updates at different times. If each device simply applied incoming operations in whatever order they arrived, the final states would diverge, since one client might apply A, C, D, B and another might apply B, A, D, C.

Back end systems traditionally deal with this by enforcing strong consistency, which relies on heavy coordination and complex algorithms. The talk instead advocates eventual consistency, where the system accepts that multiple timelines exist and is designed so that once every device has seen all the same changes, they all converge to the same state, regardless of the order in which those changes arrived.

Eventual consistency means every copy of the data ends up the same after all changes have been delivered, even if they were applied in different orders.

To get convergence under reordering, each change needs a timestamp that encodes its position relative to other changes on that device, not a wall clock time. The timestamp must capture what events the device had already seen when the new change was made, so that later merges can respect causal order.

The solution is to use logical clocks, such as vector clocks or hybrid logical clocks, that exist per device and generate timestamps that can be compared using a simple less-than comparison. The talk focuses on hybrid logical clocks, which produce string timestamps that combine physical time with logical counters while remaining easy to serialize and compare.

A hybrid logical clock is a per-device counter that mixes real time and a logical sequence so you can tell which of two events happened "later" without relying on a perfectly accurate clock.

Each change gets an HLC timestamp, and operations like "set X to value" are ordered by comparing these timestamps. In a last-write-wins strategy, the change with the larger timestamp wins. The important point is that these timestamps are not trusted as actual times, only as a consistent way to order events, and the full implementation can fit in a couple hundred lines of JavaScript without dependencies.

Even with reliable ordering, conflicts still happen when two devices set the same field while offline and then sync later. Many existing systems hand this problem off to developers by requiring manual conflict resolution logic, but the speaker argues this is unrealistic and error-prone, because conflict handling is subtle and must be designed into the data model from the beginning, not tacked on afterward.

Conflict-free replicated data types are presented as the solution to conflict handling in a distributed setting. These are special data structures that are designed so that concurrent updates can always be merged automatically in a well-defined way.

A CRDT is a data structure that you can copy to many devices and update in any order, and it will still end up the same everywhere when you merge the changes.

The specific flavor of these structures that matters in practice is defined by two properties. Operations must be commutative, meaning applying changes in different orders gives the same result, and idempotent, meaning applying the same change more than once does not change the result after the first time.

Commutative means you can swap the order of two operations and still get the same final state.

Idempotent means doing the same operation multiple times has the same effect as doing it once.

An example structure is a last-write-wins map. Here, each update to a property carries a timestamp, and when applying a change, the system checks whether the new timestamp is later than the one already stored. If it is later, it overwrites the value; if it is earlier, it is ignored. Because only the update with the newest timestamp is used per property, applying the same set of updates in any order yields the same map.

A last-write-wins map is a key-value map where, for each key, the value from the newest timestamp always wins over older values.

Another example is a grow-only set, where elements can be added but never removed. Duplicate additions have no effect because membership is just true or false for each id, and once true it stays true. In a distributed setting, the reason nothing is ever removed is that future changes might still reference an element that has not yet been seen locally, so permanent deletion would make merging unsafe.

A grow-only set is a set where you can only add elements and never delete them, so any number of adds for the same element has the same effect as one add.

To bring these ideas into a relational world, a SQLite table is treated as a grow-only set of last-write-wins maps, one map per row. The concrete implementation adds a single messages table to the database that records every change ever observed, whether created locally or received from another device.

Each message row contains a timestamp, the target dataset or table name, the row id, the column name, and the new value. Applying a message is conceptually like selecting a cell at (table, row id, column) and writing the value there, but only if the timestamp is newer than whatever has been recorded before for that cell.

If a message refers to a row id that does not yet exist, the system creates that row on the fly and sets the specified column. Over time, as more messages arrive, rows get more fields filled in, so the full relational data structure emerges from this stream of CRDT updates.

Reads stay simple and fast because the app still uses plain SQLite queries to access the reconstructed tables. Writes are routed through helper functions such as an update function that takes the table name, row id, and changed fields, generates messages with timestamps, and feeds them through the same sync pipeline that handles incoming messages from other devices.

Deletion is handled using tombstones instead of actually removing rows. A delete function generates a message that sets a special tombstone field on the row to 1, and read queries are written to ignore rows with the tombstone set. The row remains present in the underlying grow-only set so that future sync operations can still reason about it correctly.

A tombstone in this context is a flag on a record that marks it as deleted without physically removing it from the data set.

To keep devices efficiently in sync, the system can use a Merkle tree built over the set of timestamps. This tree of hashes summarizes which changes a device has seen, so two clients can quickly compare trees to figure out what messages they are missing and only exchange the necessary differences.

A Merkle tree is a tree of hashes that lets two sides compare large sets of data by comparing small hash values instead of every item.

The architecture lends itself to end-to-end encryption and a very lightweight sync server, because the server only needs to accept messages and send them back out, without needing to understand or inspect the actual data contents.

The talk emphasizes that data shapes should be designed to avoid conflicts altogether when possible. For example, a mapping table can be used so that category ids from different devices are mapped into a canonical set of categories, ensuring that cases like "item added in a category that was deleted elsewhere" resolve automatically to a safe default without manual conflict code.

The resulting sync implementation is surprisingly small: the server side is roughly a hundred lines of JavaScript that just stores and forwards messages, and the client side - including database handling, clocks, and CRDT logic - is only a few hundred lines with minimal dependencies. This demonstrates that robust local-first sync can be achieved with compact, understandable code.

The conclusion is that fully local applications provide a far superior experience in speed, offline behavior, privacy, and flexibility, and developers are encouraged to explore this direction using CRDTs, simple logical clocks, and deliberately small implementations instead of relying on complex, heavyweight systems.

2025-11-30 John Mumm - A CRDT Primer: Defanging Order Theory - YouTube { www.youtube.com }

image-20251129235241699


Imagine we are building the Birdwatch app from the talk: people click a little bird icon on a post, and we want to count how many times that has happened across several servers.

Step 1: We decide that each server will keep its own local copy of the counter, but instead of storing a single integer, each server stores a vector of integers. If we have three servers, the state looks like [c0, c1, c2], where c0 is how many clicks server 0 believes it has processed, c1 is how many clicks it believes server 1 has processed, and so on. At the very beginning, all servers start at [0, 0, 0].

Step 2: A user request to click the bird hits server 0. Server 0 handles that click by applying the local update. The update rule is: "increment my own slot in the vector." Since this is server 0, it increments the first component and changes its local state from [0, 0, 0] to [1, 0, 0]. The other servers have not seen this yet, so they still sit at [0, 0, 0].

Step 3: Another user click arrives at server 2. Server 2 uses the same rule, but on its own index. It increments the third component and changes its local state from [0, 0, 0] to [0, 0, 1]. Now the system has two different local views: server 0 believes the state is [1, 0, 0], server 2 believes it is [0, 0, 1], and server 1 still believes [0, 0, 0].

Step 4: Periodically, servers gossip their state to each other. Suppose server 0 sends its state [1, 0, 0] to server 2. When a server receives a remote state, it merges it into its own local one using the merge function. The merge rule is: "take the componentwise maximum." Server 2 merges [0, 0, 1] (its own) and [1, 0, 0] (received) and gets [max(0,1), max(0,0), max(1,0)] = [1, 0, 1]. After this merge, server 2 now knows that server 0 has seen one click and server 2 itself has seen one click.

Step 5: At any point, a client can ask a server, "what is the current value of the counter?" The rule for answering is simple: sum all components of the local vector. For server 2, which now holds [1, 0, 1], the visible count is 1 + 0 + 1 = 2. That is exactly the total number of clicks the whole system has processed so far, even though not all servers know this yet.

Step 6: A third click arrives, this time at server 1. Using the same update rule, server 1 increments its own slot and changes its local state from [0, 0, 0] to [0, 1, 0]. Now the true global situation, if we conceptually add everything up, is three clicks: one at server 0, one at server 1, and one at server 2. But the replicas do not yet all agree.

Step 7: Gossip continues. Suppose server 1 sends [0, 1, 0] to server 0. Server 0 merges its own [1, 0, 0] with that remote state componentwise and gets [max(1,0), max(0,1), max(0,0)] = [1, 1, 0]. Server 0 now believes one click happened on itself and one on server 1, but still knows nothing about server 2. If a client queries server 0 at this moment, it answers 1 + 1 + 0 = 2, which is slightly behind the real total of three, but it is not wrong with respect to anything it has seen.

Step 8: Later, server 2 gossips [1, 0, 1] to server 1. Server 1 merges [0, 1, 0] and [1, 0, 1] and gets [1, 1, 1]. Now server 1 has a full picture: one click per server. A query to server 1 now gets 1 + 1 + 1 = 3, which matches the true global count. Nothing has forced all servers to synchronize at once; this has happened through normal asynchronous gossip and merging.

Step 9: Eventually server 1 will gossip [1, 1, 1] to server 0 and server 2. When they merge, both will also obtain [1, 1, 1]. At that point all replicas agree, but the key point is that this agreement was not required for correctness at intermediate steps. Every merge was just a componentwise max; every local update only increased one component; and any sequence of these operations keeps moving the state upward in the partial order defined by comparing vectors componentwise.

Step 10: Because the merge is associative, commutative, and idempotent, the final state each replica converges to does not depend on the order of gossip messages, nor on whether some states are received multiple times. Re-merging [1, 1, 1] with [1, 1, 1] does nothing, since the max of equal components is the same number. Delayed messages do not break anything; when an old state finally arrives, merging it with a newer state simply keeps the newer information because the newer components are greater.

Step 11: From the client perspective, the counter behaves very naturally. When they click, the local node increases its own component, so the next read from that same node will show a value at least as large as what they saw before, often strictly larger. As more gossip completes, reads from any node monotonically rise toward the true total. There is no possibility of the count going down, and no risk that merging creates phantom extra clicks, because every update is a local increment and every merge is a join that preserves the maximum seen at each replica.

Step 12: The same pattern works for more complex replicated structures. Once you choose a state representation with a partial order, define a merge that is the join in that order, and design updates that only move states upward, you obtain the same behavior: independent replicas, asynchronous gossip, arbitrary reordering and duplication of messages, and eventual convergence on a coherent global value without coordination.


At the heart of this structure there are three moving parts: the update function, the merge function, and the value function. Each has a very simple job, and the combination of those jobs is what makes the whole thing work in a hostile distributed system.

First, think about the order we put on states. For the G-counter, each state is a vector like [c0, c1, c2]. We say one state is less than or equal to another when every component is less than or equal componentwise. So [1,0,1] <= [2,0,3] because each position is less than or equal. But [1,3,0] and [2,1,0] are incomparable, because sometimes the arrows go up, sometimes they go down.

A state s1 is below s2 in the order if every component of s1 is less than or equal to the corresponding component of s2.

Now look at the update function. On node i, update is “add one to component i, leave the rest alone.” If the old state is v and the new state is v', then every component except i is identical, and component i has increased by one. That means v is always less than or equal to v' in our componentwise order. The state never moves sideways or down; an update always pushes it strictly upward.

The update function is monotone: applying it produces a new state that is greater than or equal to the old one in the order.

Because updates only move upward, they never erase information. A click that has been recorded in some component is never undone by a later update anywhere. If you imagine the partial order as a graph, every update is a step along one of the arrows that go upward.

Next, the merge function. Merge takes two states and computes the componentwise maximum. If we merge [1,0,2] and [0,3,1], we get [max(1,0), max(0,3), max(2,1)] = [1,3,2]. This merged state is exactly the least upper bound of the two in our order: it is above both input states, and it is the smallest state with that property. So merge is literally implementing the join operation of the join-semilattice.

The merge function is the join: it returns the smallest state that is above both of its arguments in the order.

Because merge is a join, it obeys three key algebraic laws. It is associative, so (a merge b) merge c is the same as a merge (b merge c). That means if node A gossips to B, then B gossips to C, you get the same combined information as if A had gossiped directly to C and then C merged with B later. It is commutative, so a merge b equals b merge a; it does not matter which direction the message flowed or which replica is considered “left” or “right” in the code. And it is idempotent, so a merge a is just a; if the same state is transmitted twice and merged twice, nothing changes the second time.

Idempotence means merging the same information again does not change the state.

Those three properties of merge are exactly why message reordering, duplication, and fan-out do not break convergence. Any finite pattern of gossip is equivalent to “take the join of all states you have ever seen,” because you can regroup (associativity), reorder (commutativity), and drop duplicate merges (idempotence) without changing the result. The unique result of “join everything” is the least upper bound of all the replica states at that moment, which we can think of as the ghost global state.

Now combine update and merge. Every local update moves a state upward. Every merge also moves the receiving state upward or leaves it where it is, because the result is an upper bound of the two arguments in the order. There is no operation in the system that moves a state downward. So if you watch any one replica over time, its state follows some path that only climbs in the partial order, sometimes by local increments, sometimes by merges. If you conceptually join all the states that have been created so far, you get the current ghost global upper bound. And because each replica is repeatedly joining in more and more of these states through gossip, its own local state keeps moving toward that upper bound.

Because every transition is monotone, any sequence of updates and merges always moves replicas upward toward the current global upper bound.

The last piece is the value function. For the G-counter, value is “sum all components of the vector.” If state v is below state w in the componentwise order, then every component of v is less than or equal to the corresponding component of w, so the sum of v is less than or equal to the sum of w. That means the value function itself is monotone with respect to the state order: when the state goes up, the visible integer count never goes down.

A monotone value function never reports a smaller observable value when the underlying state becomes larger in the order.

This is precisely why a client never observes the counter decreasing. When you click on a post, your node applies an update, which raises its local state. The next time you ask for the value on that node, the state cannot be lower than it was before, so the sum cannot be lower either. Later, when gossip brings in information about clicks seen on other nodes, merges will raise the local state again, and the sum will increase again. At worst, the value you see is a little behind the ghost global sum because you have not yet heard about all remote updates, but it is always consistent with some prefix of the system’s history and always nondecreasing from your point of view.

Putting all of this together, the structure works because of a very tight alignment between these three parts. The state space with its order is a join-semilattice. Merge computes joins in that semilattice and so has the algebraic properties that tolerate arbitrary gossip. Update is monotone in that same order, so it moves states up without breaking the lattice structure. Value is also monotone, so as states climb, observable values climb too. Given those three conditions, no matter how many nodes you have, no matter how messages are reordered, delayed, or duplicated, all replicas are always climbing toward the same upper bound, and all clients see counters that only move forward.

2025-12-02 Conflict-Free Replicated Data Types (CRDT) for Distributed JavaScript Apps. - YouTube { www.youtube.com }

Speaker: https://jonathanleemartin.com/

2025-12-02 coast-team/dotted-logootsplit: A delta-state block-wise sequence CRDT { github.com }

2025-12-02 automerge/automerge: A JSON-like data structure (a CRDT) that can be modified concurrently by different users, and merged again automatically. { github.com }

image-20251201233349962 image-20251201233518746


Conflict-free replicated data types are presented as a different paradigm for handling concurrent edits: instead of transforming operations like in operational transformation, they define data structures whose update rules guarantee that all replicas converge without needing a special conflict-resolution step.

A conflict-free replicated data type is a way of storing data so that many copies can be edited independently and still end up in the same state automatically.

Basic instances such as sets and counters are described as largely solved, and are already used in systems like distributed databases; however, creating good structures for ordered sequences such as text is much harder and has driven a lot of recent research.

One particular sequence design, called LSEQ (linear sequence), is highlighted as a favorite because it aims to be fast, memory-efficient, and suitable for multi-peer collaboration without a central server.

LSEQ is a tree-based representation of a sequence that assigns each element a carefully chosen position identifier so replicas can merge edits consistently.

Instead of representing text as a simple array of characters, this approach uses a special exponential tree (based on the Logoot tree): a root with multiple numbered branches at each level, where characters live at leaves, and a left-to-right depth-first traversal of the tree yields the visible string.

In this scheme there is a clear split between the model and the view: the underlying tree is the model optimized for correctness and performance, while the string shown to the user is a view obtained by traversing that structure.

The model is the internal data structure the algorithm manipulates, while the view is the human-readable form derived from it.

Each character node is addressed by an identifier constructed from the sequence of branch labels taken from the root to that node (for example, "3.7.7" might name one letter), and this identifier serves as a permanent, unique name for that position.

An identifier here is a structured label that uniquely and permanently names a position in the collaborative document.

When inserting between two characters, the editor chooses a new identifier that sorts between their identifiers (for example, inserting between "3.7.7" and "3.8" might produce "3.7.9"), adds a new node in the appropriate place in the tree, and associates the inserted character with that new name.

When replicas exchange operations, they do not transform indices; they simply share the inserted or deleted identifiers, and each participant integrates them into its own tree and then traverses that tree, so that all end up with the same character order even if operations are applied in different orders.

The core idea is that the total order over identifiers replaces explicit conflict resolution rules.

For this to work, the identifiers must be immutable and unique: once a name is assigned to a position, it can never change or be reused, because later merges may depend on that exact label as a stable reference.

Immutability here means that once a position label is created, it never changes value.

Because identifiers are never reused and insertions may require creating names between existing ones, their length and the depth of the tree can grow as the document is edited; if this were done naively, the tree could degenerate into something like a linked list with poor performance.

LSEQ therefore focuses heavily on an allocation strategy for identifiers: it uses an exponential branching pattern and some randomness so that, even under adversarial editing patterns, the tree stays roughly balanced, identifier growth is slow, and lookups remain close to logarithmic time.

A key advantage over central-server operational transformation is that this tree-based sequence allows true multi-peer operation: any number of replicas can edit offline and then synchronize changes directly with one another, yet still converge without relying on a single authoritative server.

Another advantage is that it does not require an explicit tombstone mechanism with coordinated garbage collection; instead, because identifiers encode position independently of the current tree, nodes associated only with deleted content can eventually be dropped locally without a global clean-up phase.

A tombstone is a marker that remembers where a deleted element used to be so later operations that refer to it can still be interpreted.

In the question-and-answer discussion, it is clarified that deletions do still leave structural traces: if a node has children, its character payload is removed but the node stays as a kind of implicit tombstone until all possible descendants are gone, after which the structure can naturally disappear from that replica.

The same discussion explains that even if a subtree was removed on one machine, a later operation from another replica that references an identifier inside that region can reconstruct the necessary path from the identifier itself, because the position is encoded in the ID rather than in surrounding context.

To keep different editors from picking exactly the same position name, identifiers include more than just the tree path: they also contain a replica or site ID, a local counter, and possibly causal metadata, which together ensure uniqueness and provide a deterministic tie-breaker when different users choose numerically similar positions.

Replica identifiers and counters let the system break ties in a consistent way whenever different users generate conflicting-looking position labels.

The algorithm has non-obvious performance costs: every time a user inserts at a given character index in the visible text, the system must map that view index to the correct node in the tree, and this mapping step can be more expensive than the ideal logarithmic time suggested by the tree shape.

There is also a behavioral caveat: if two people independently insert different words at the same visual position while offline, the merged result may consist of their letters interleaved character-by-character, producing a structurally valid but linguistically unnatural string that does not match either person`s intention.

Subsequent research has built on this scheme, leading to newer sequence structures such as dotted Logoot-split AVL trees that aim to reduce identifier growth, improve memory usage, and mitigate interleaving problems while preserving the same convergence guarantees.

Practical JavaScript implementations now exist: tree-based text CRDTs like logoot-split offer LSEQ-style behavior for documents, while a library such as Automerge provides a CRDT for arbitrary JSON data, so application state or Redux-like stores can be replicated and merged without conflicts at the structural level.

Automerge treats strings as lists and uses a different sequence algorithm (RGA-split) that relies on tombstones; this works well for many small text fields such as card titles or descriptions but is not ideal for very long, heavily edited documents because the tombstone history can grow large in memory.

Beyond linear text, these ideas generalize to many collaborative domains: any state that can be modeled as sets, counters, ordered sequences, or nested JSON-like structures can be given CRDT semantics, enabling distributed editing of things like boards, documents, diagrams, or even music notation.

The speaker stresses that structural convergence does not automatically guarantee semantic correctness; designers still need to choose good data models, so that even when different edits are merged mechanically, the resulting state respects as many application-level invariants as possible, with manual conflict resolution reserved for rare edge cases.

Finally, the algorithms rely only on per-replica ordering of operations, typically via local counters or timestamps; there is no need for globally synchronized clocks, and batching operations for network transmission is independent of the logical order used for merging.


A00 Let us first fix a simple mental model. Think of an LSEQ style CRDT as keeping two things at once. There is the view, which is the text the user sees, like "cat". Underneath there is the model, which is a list of characters, and each character has a special position id that can be compared and sorted. When two replicas merge, they do not argue about indices like "insert at index 1". They only collect all characters with their ids, sort by id, and the sorted order defines the final text.

A01 A position id in LSEQ is not just a single number. It is a small vector of integers, like [3] or [3,5] or [10,2]. Two ids are compared lexicographically: first element, then second, and so on. Between two ids you can often create a new id that sorts between them by choosing new numbers or by going one level deeper. This ability to always find a fresh id between two existing ids is the key that lets many users insert in the "same" place without conflicts.

A02 For this example, there will be two users, Alice and Bob. They both start from the same initial document containing the text "cat". At the model level, the document is stored as a list of entries of the form (id, character). At the beginning we will assume some simple ids, chosen by a library when the document was created:

(id: [3], char: "c") (id: [7], char: "a") (id: [11], char: "t")

A03 The view is the string that results from sorting these entries by id and reading out the characters: [3] < [7] < [11], so the text is "cat". Alice and Bob both see exactly this text and those same ids at the start.

A04 Now both go offline and make edits concurrently. Alice wants to insert the letter "h" right after "c" to start writing "chat". In the view, that means insert "h" at index 1. In the model, Alice looks at the two neighboring ids: the "c" has id [3], the next character "a" has id [7]. LSEQ asks: choose a new id that compares strictly between [3] and [7]. There are many choices; a simple one is [5]. So Alice creates a new entry:

(id: [5], char: "h")

and inserts it in her local structure between [3] and [7]. Locally her model is now:

[3] "c", [5] "h", [7] "a", [11] "t"

and her view shows "chat".

A05 At the same time, Bob wants to insert the letters "r" and "e" after "c" to write "cr eat" on his side. For illustration, let us say Bob first inserts "r" after "c", then "e" after that. When he inserts "r", he also looks at ids [3] and [7], just like Alice did, but his local random strategy picks a different id between them, say [4]. So after inserting "r" he has:

[3] "c", [4] "r", [7] "a", [11] "t"

and his view shows "crat" for a moment.

A06 Then Bob inserts "e" right after "r". In the view this is between "r" and "a". In the model this is between ids [4] and [7]. He can choose a new id between [4] and [7], say [6]. After that change, his model is:

[3] "c", [4] "r", [6] "e", [7] "a", [11] "t"

and his view shows "creat". So now, offline, Alice sees "chat" and Bob sees "creat".

A07 Each replica has also recorded the operations it made in terms of CRDT events. An event is something like "insert character 'h' at id [5]" or "insert character 'r' at id [4]". Notice that the event contains the chosen id, not "insert at index 1". The index is only a local convenience for the user; the id is the durable address.

A08 When Alice and Bob come back online, they exchange their operations. The important point: they do not need to know the exact order in which things happened in real time, and they do not need to transform indices. Each side simply takes the operations that came from the other side and applies them to its own model.

A09 Consider what happens on Alice’s side when she receives Bob’s operations. Her model currently has entries:

[3] "c", [5] "h", [7] "a", [11] "t"

She receives an operation "insert 'r' at id [4]" and an operation "insert 'e' at id [6]". The application rule is very simple: add these new entries into the set keyed by id, assuming those ids are not already present. After inserting both, Alice’s local list of entries is:

[3] "c", [4] "r", [5] "h", [6] "e", [7] "a", [11] "t"

A10 To build the view, Alice sorts by id. Lexicographically [3] < [4] < [5] < [6] < [7] < [11], so the visible text becomes "crheat". This looks odd in English, but structurally it is fully defined by the ids and independent of the order in which the operations arrived.

A11 On Bob’s side the same thing happens in the other direction. His model currently has:

[3] "c", [4] "r", [6] "e", [7] "a", [11] "t"

He receives Alice’s operation "insert 'h' at id [5]". That id is not present yet, so Bob adds the new entry and now has:

[3] "c", [4] "r", [5] "h", [6] "e", [7] "a", [11] "t"

He also sorts by ids and his view becomes "crheat". Even though the messages may have arrived in a different order, the final sorted order by id is exactly the same as on Alice’s side.

A12 This is how convergence is achieved. The CRDT guarantees two properties. First, every replica eventually receives the same set of operations, which means the same set of (id, character) pairs. Second, the order is determined only by a pure function over these immutable ids. So as long as id generation follows the same rules on all replicas, sorting will always produce the same sequence of characters.

A13 The example above uses only one level numbers like [3], [4], [5], [6], [7]. In practice, you will eventually need to insert between two ids that have no free integer between them. For example, suppose you have ids [3] and [4], and someone wants to insert one more character between them. There is no integer strictly between 3 and 4, so LSEQ drops to a deeper level. It creates a two-component id, for example [3,5]. When you compare ids, [3,5] still sorts between [3] and [4], because first you compare the first component: 3 equals 3, then you compare the second component, 5, which is less than 4 by definition of how that level is constructed.

A14 Because ids are vectors of integers and not just single numbers, the CRDT can always proceed by adding a deeper component when it runs out of space at the current level. Different replicas can choose different random numbers for these components and still end up with a total order because the tie breaking rules are deterministic. Over time this gives you a tree shape inside the id space, even though you interact with it as a sorted sequence.

A15 Deletion works in a similar fashion. There is no "delete at index 2" in the CRDT protocol. There is "delete the character that has id [5]". When Alice deletes that character, she marks id [5] as removed in her model. When Bob later receives this deletion operation, he also removes the character associated with id [5] from his model. Since both replicas drop the same id, they both agree on which character disappeared. They then sort the remaining ids and reach the same view.

A16 This example shows the main ideas without the full tree representation. The model stores characters keyed by immutable ids. Insertion generates a fresh id between two neighboring ids. Deletion removes the character for a specific id. Replicas exchange operations that mention these ids, not raw indices. Each replica independently maintains a set of entries and derives a view by sorting them. Because sorting is deterministic and ids never change, all replicas converge to the same sequence once they have seen the same operations, even if their edits were concurrent and even if messages arrive in different orders.


To make LSEQ-style ids immutable and unique you mainly have to decide 2 things: what the id looks like, and how you allocate a new one between two existing ids.

A practical shape for an id is a triple: a path, a replica identifier, and a local counter. The path is the list of integers you already saw, for example [3], [3,7], [3,7,9]. The replica identifier is a value that is globally unique per device or browser session, such as a UUID or random 128-bit number. The counter is a number that starts at 0 on each replica and is incremented every time that replica allocates a new id. Once you create a triple (path, replicaId, counter) you never change any part of it again; that is where immutability comes from.

Uniqueness comes from combining the path with the replica identifier and counter. Two replicas may occasionally choose the same path when inserting between the same neighbors, but because they have different replica identifiers they still produce different overall ids. Even on a single replica, the counter ensures you never accidentally reuse the same (path, replicaId) pair twice. When you compare ids to sort characters, you use a fixed lexicographic rule: first compare paths component by component; if the paths are equal, compare replicaId; if still equal, compare counter. That comparison rule is the same on every replica, so they all derive the same total order.

The interesting part is how to generate a new path between two neighbors. Suppose you want to insert between left path L and right path R. At some depth d you look at the d-th component of L and R. If they differ and there is room between them, you choose a random integer between them at that depth, and copy the prefix from the left side. For example, if L is [3] and R is [7], you can choose 4, 5, or 6 as the new component and get a path [5]. Because 3 < 5 < 7, [5] will sort between the two neighbors on all replicas. If there is no room between L and R at the current depth, you go one level deeper. For example, if L is [3] and R is [4], there is no integer strictly between 3 and 4, so you extend the shorter path. You might treat L as [3,0] and R as [4,0] conceptually, or you might define a depth-dependent base, and then pick a number between those new bounds at the deeper level, for example [3,5]. Now [3,5] still sorts between [3] and [4] because you compare 3 with 3 first, then 5 with the implicit 4 at that depth according to your scheme. The LSEQ paper chooses these ranges carefully and often uses randomness within them so that, over time, insertions are spread out and the tree stays fairly balanced.

Once you have a procedure like that, generating a new id when a user inserts a character looks like this in words. You take the id of the character just before the insertion point and the id of the character just after it. You run your “allocate path between L and R” procedure to get a fresh path. You increment your local counter. You form the id as (newPath, replicaId, counter). You attach that id to the inserted character. After that, that id is frozen forever: you never edit it, and you never reuse it.

Deletions then talk only about existing ids. When a user deletes a character, the operation you broadcast is “delete id X”. Every replica that receives that operation finds the entry with id X and removes it or marks it deleted. Because ids never change and are globally unique, every replica removes the same character. Insertions only ever introduce new ids, never modify old ones. Deletions only ever remove existing ids, never create new ones. As replicas exchange these operations, each one ends up with the same set of ids and associated characters, and because the comparison rule is deterministic and based on immutable fields, sorting them always yields the same order.

So the recipe is: design ids as immutable tuples that include a path, a globally unique replica identifier, and a per-replica counter; define a deterministic lexicographic comparison over those fields; implement a “pick a path between two paths” function that can always find a new path by going deeper when necessary and that tends to keep the tree balanced. With those three pieces in place, the ids are naturally immutable, unique, and sufficient to drive convergence.

2025-12-02 CRDT Papers Conflict-free Replicated Data Types { crdt.tech }

This page contains a comprehensive list of research publications on CRDTs. The data is available in BibTeX format. If you have anything to add or correct, please edit the file on GitHub and send us a pull request. image-20251201235239094

2025-10-27 Conflict-Free Replicated Data Types (CRDTs): Convergence Without Coordination { read.thecoder.cafe }

Welcome to The Coder Cafe! Today, we will explore CRDTs, why they matter in distributed systems, and how they keep nodes in sync. Get cozy, grab a coffee, and let’s begin!

image-20251026213014196Concurrency is about causality, not timing Two operations are concurrent if neither was aware of the other — regardless of when they happened. For example, edits made hours apart can still be concurrent if there was no shared knowledge of each other's changes. Action: Classify operations as concurrent by checking if they were causally dependent, not by timestamp.

Coordination is costly and optional with CRDTs Traditional systems need replicas to coordinate to agree on one valid result before responding, which delays responses. CRDTs remove this need by defining deterministic merge functions, enabling replicas to process updates locally. Action: Use CRDTs when you need immediate local writes, even under network partitions.

CRDTs ensure Strong Eventual Consistency (SEC) CRDTs use deterministic merge rules that ensure all replicas converge on the same result without central coordination. Action: Choose CRDTs when you want high availability and are willing to trade off strong immediate consistency.

G-Counter shows simple merge rules Each node only increments its own counter. Merging is done by taking the element-wise maximum, ensuring all replicas converge. Action: Use G-Counter for cases like "likes" or counters where values only grow.

PN-Counter handles increments and decrements Maintains separate vectors for additions and subtractions. After merge, computes total by subtracting summed decrements from increments. Action: Use PN-Counter for distributed systems that need to both increment and decrement reliably.

Three CRDT sync models exist

  • State-based: send full state and merge using associative, commutative, idempotent logic.
  • Operation-based: send ops like "add 5", needing causal delivery.
  • Delta-based: send only changed fragments. Action: Match the sync strategy to your bandwidth and delivery guarantee constraints.

Offline collaboration is a natural CRDT use case CRDTs let users make changes while offline and merge later without conflict. Notion and other modern tools leverage this for seamless collaboration. Action: Implement CRDTs in editors and UIs where users may go offline.

Active-active replication is CRDT-backed Systems like Redis use CRDTs to support multi-region writes with local latency and no central authority. Action: Apply CRDTs in systems needing cross-region availability and latency-sensitive updates.

CRDTs vs Operational Transformation (OT) OT needs a central arbiter to coordinate edits. CRDTs allow fully decentralized, offline-safe updates. Action: Use CRDTs for peer-to-peer or offline-first architectures.

Beyond text: CRDTs fit edge and IoT scenarios CRDTs naturally support devices that store local state and sync later, like IoT or CDN edge caches. Action: Consider CRDTs in environments where connectivity is intermittent or decentralized coordination is impractical.

2025-12-06 Microsoft Research Video 153540 Strong Eventual Consistency and Conflict free Replicated Data Types - YouTube { www.youtube.com }

image-20251206114520819

🌈Year 2011 Marc Shapiro

The talk explains how to build very fast, highly scalable replicated data structures in the cloud by relaxing traditional consistency guarantees, so that replicas can update their local state without coordination yet still converge to the same value later.

Strong consistency is described as the model where all updates are totally ordered, so every replica sees the same sequence of operations and always has the same view of the world, often called linearizability or sequential consistency.

Strong consistency: every operation appears to run one after another in a single global order.

To implement this total order, systems use consensus protocols that force all participants to agree on each next step, turning a parallel system into a logically sequential one and creating both a performance bottleneck and a reliability bottleneck because progress depends on a majority of nodes being alive.

Consensus: a protocol by which multiple nodes agree on a single value or decision.

Eventual consistency arose as a way to avoid putting consensus on the critical path: replicas can update independently, diverge for a while, then later reconcile their differences with a global arbitration step that resolves conflicts, often again using some form of consensus in the background.

In this weaker model, a replica may temporarily see invalid or conflicting states; reconciliation can be complex, may roll back previous outcomes, and can be difficult to design correctly despite the apparent simplicity of letting updates proceed without initial coordination.

Eventual consistency: if updates stop, all replicas will eventually hold the same state, but what happens before that is unconstrained.

Strong eventual consistency is introduced as a stricter and more useful variant: as soon as two replicas have received the same set of updates, they must already be in the same state, with no rollbacks or later corrections.

Under this model, updates are applied locally without synchronization, but the way operations are defined guarantees that any replica that has seen the same operations will converge to exactly the same result, independent of delivery order or interleaving.

Strong eventual consistency: replicas that have applied the same updates are already identical.

With strong eventual consistency, the classical CAP tradeoff is reframed: instead of choosing between strong consistency and availability under network partitions, one can keep availability and partition tolerance and still have a well-defined consistency property, provided one is willing to adopt this weaker but deterministic convergence model.

The talk formally characterizes traditional eventual consistency with three properties: every operation is eventually delivered everywhere, every operation eventually terminates, and replicas eventually converge when updates stop; strong eventual consistency keeps the first two but strengthens convergence to hold immediately once the same updates have been seen, eliminating the need for rollbacks.

To realize strong eventual consistency, the speaker proposes designing special data types whose operations guarantee convergence without coordination; these are called conflict-free replicated data types, or CRDTs.

CRDT: a replicated data type whose operations are designed so that replicas converge without needing coordination.

Two replication styles are considered: state-based and operation-based. In the state-based style, replicas occasionally send their entire state (or summaries) to other replicas, which merge the received state with their own using a merge function defined by the data type.

For state-based replication, a simple mathematical condition guarantees convergence: the local payload must form a join-semilattice, every update must move the state monotonically upward in that partial order, and the merge function must compute the least upper bound of the two states.

Semilattice: a set with a partial order and an operation that gives a least upper bound for any two elements.

This condition means that replicas can exchange states in any pattern, repeatedly, and still converge, because each merge moves them upward in a way that never loses information and never cycles.

For operation-based replication, replicas broadcast update operations instead of full state; each operation is applied at its origin and then delivered to all other replicas, which replay it on their local state.

In the operation-based style, a sufficient condition for strong eventual consistency is that all concurrent operations commute, while operations that are causally ordered must be delivered and applied in that same causality order at every replica.

Commutativity: two operations commute if applying them in either order gives the same result.

The talk explains that these two conditions, one for state-based and one for operation-based replication, are in fact equivalent: any state-based CRDT can be emulated in an operation-based system and vice versa, and convergence in one model implies convergence in the other.

State-based specifications are often easier to reason about mathematically, while operation-based implementations are usually more efficient in practice, because they avoid sending entire state and can instead send small deltas or individual operations.

The compositional properties of CRDTs are highlighted: the product of two independent CRDTs is again a CRDT, and a single CRDT can be partitioned into independent components that remain CRDTs, which directly supports static sharding of large data structures across many nodes.

This compositionality underpins a clean story for scaling: a large structure, such as a graph, can be partitioned deterministically across shards by a hash function, with each shard maintaining its own CRDT state, as long as the partitioning remains static or any re-partitioning is done with some coordination.

The speaker emphasizes that the CRDT conditions are not just sufficient but effectively necessary for strong eventual consistency: if a data type allows two replicas to apply the same concurrent updates and still end up consistent regardless of delivery order, then those concurrent operations must commute for all initial states and arguments.

The talk then explores concrete CRDT designs, starting with counters. Even for a simple grow-only counter, the state-based approach requires more structure than a single integer: each replica keeps a vector of per-replica counters, with each replica only incrementing its own entry, and merge taking elementwise maxima.

The value of such a grow-only counter is the sum of the entries in this vector, the partial order is the usual componentwise order between vectors, and each increment moves the state upward, so merge-as-max satisfies the semilattice condition and convergence is guaranteed.

To support both increments and decrements while staying within the semilattice framework, the design uses two grow-only counters, one tracking increments and one tracking decrements; the exposed value is the difference between these two internal counters, and each update only ever grows one of the components.

The talk stresses that naïve approaches, such as using a single counter that increments and decrements arbitrarily and merging by taking maxima, do not work because decrements would be lost or behave as no-ops, violating the intended semantics.

Sets provide a richer case study. Sequentially, a set offers add and remove operations with the obvious invariants: after add(e), element e is in the set; after remove(e), e is not; the challenge is to define what should happen for concurrent add(e) and remove(e) operations on different replicas.

Several conflict-resolution strategies are possible: mark the result as an error, use last-writer-wins based on timestamps, choose add-wins, or choose remove-wins; all are consistent strong eventual semantics, and the right choice depends on the application.

The talk focuses on an add-wins design that closely matches intuitive expectations for many applications: when an add and a remove are concurrent, the element should remain present, because the remove could not have seen the add.

To realize add-wins semantics as a CRDT, the observed-remove set (OR-set) is introduced. Each call to add(e) actually creates an internal instance of e tagged with a unique identifier, and remove(e) removes only those internal instances of e that were observable in the local state at the time of the remove.

Tombstone: a marker that an element instance was removed, kept so that later merges can recognize that removal.

In this OR-set, the internal state includes both live element instances and tombstones for removed instances; merge is defined as a union of these internal records, and the externally visible membership test for e only checks whether there is any live instance of e that has not been tombstoned.

Because a remove can only tombstone internal instances it has observed, a concurrent add of e with a fresh identifier that the remove did not see will survive, so after all updates are propagated and merged, e remains in the set, giving the intended add-wins behavior.

The talk explains why simpler ideas like using a per-element counter (increment on add, decrement on remove) can fail in the presence of concurrency: different replicas may both decrement based on the same single add, causing the counter to go negative or to misrepresent membership when operations are merged.

The need to keep tombstones raises garbage-collection concerns. The speaker notes that vector clocks or version vectors can be used to track causality, allowing implementations to detect when a deletion has been seen by all replicas, at which point the corresponding tombstones can be safely discarded in optimized implementations.

A practical motivation is given through Amazon Dynamo’s shopping cart example. Dynamo used a multi-value register that stored a set of possible values instead of a true set CRDT, which can cause removed items to reappear; a correctly designed set CRDT like the OR-set would avoid such anomalies.

Building on sets, the talk constructs a graph CRDT. A graph is modeled as a pair of sets: a set of vertices and a set of edges, where each edge is a pair of vertices; sequentially, one typically enforces an invariant that edges reference existing vertices and that vertices cannot be removed while incident edges exist.

In the distributed setting, concurrency between removing a vertex and adding an edge to that vertex introduces a new kind of conflict; as with sets, there are multiple possible semantics, such as enforcing strict invariants with coordination or choosing which operation should dominate.

For modeling the web, the talk adopts add-edge-wins semantics and relaxes the invariant: operations to add edges or delete vertices are always accepted, the invariant is not enforced at update time, and instead lookups interpret the stored state, treating edges to non-existent vertices as absent.

This behavior matches URLs pointing to pages that may not exist yet or anymore and supports sharding, because add operations do not need to synchronously consult remote shards to check invariants; only lookups need to traverse shards, and they can do so on a consistent snapshot.

Consistent snapshots are achieved by using the unique identifiers of elements as timestamps, combined with version vectors: for each replica, a timestamp summarizes what it has seen, and a global snapshot is defined by choosing a vector of local timestamps that describes a cut in the distributed execution.

Given such a snapshot vector and retained tombstones, the system can answer membership or reachability queries as of that logical time, which is useful for operations like computing PageRank that require a stable view of the graph.

The talk then sketches how these ideas could structure a large-scale web indexing and search system: crawlers build local maps from URLs to content, a CRDT graph for links, and CRDT maps for words to postings; updates are propagated as operations through a dataflow pipeline.

Because CRDTs allow asynchronous updates and strong eventual consistency, the system can run many replicas and shards around the world, make progress despite partitions, and adapt resource usage by throttling or accelerating update propagation without violating convergence guarantees.

Finally, the limitations of CRDTs are discussed: they make it difficult to enforce strong global invariants or multi-object constraints, such as ensuring a bank account never goes negative or atomically transferring money between accounts without ever showing intermediate double-credit or double-debit states.

In such cases, some form of synchronization, transactions, or centralized computation on consistent snapshots is still required, and the lesson is that CRDTs and strong eventual consistency are powerful for a wide class of problems but do not replace consensus-based mechanisms for all data types or invariants.

2025-09-29 Why Local-First Apps Haven’t Become Popular? { marcobambini.substack.com }

image-20250929162449926 Local-first apps promise instant load times, resilience to flaky networks, and better privacy, but they remain rare because sync is hard. The article frames local-first as a distributed-systems problem: multiple devices mutate the same data while offline, then must converge to one state without losing intent. Two core challenges block adoption. First, unreliable ordering: operations arrive out of order, so naïve “last write wins” produces surprising losses. Second, conflicts: concurrent edits require semantics that match user expectations, not just technical convergence.

The proposed path uses Hybrid Logical Clocks (to assign a consistent happens-before order across devices) and CRDTs (to merge concurrent changes without coordination). Together they remove reliance on network ordering and allow safe, client-side merges. The piece argues that a robust local database is the right foundation and positions SQLite as a strong fit because it is embeddable, fast, and ubiquitous across platforms. With a local DB handling durability and indexes, and a CRDT/clock layer handling causality and merge, you can deliver true offline-first behavior and sync later without central coordination.

This matters because local-first improves UX (zero-latency reads/writes), reliability (works through outages), and privacy (data persists on devices). For developers, the takeaway is to treat sync as a first-class concern: model data with CRDTs where appropriate, capture causality with logical clocks, and store everything in a proven local database. The combination reduces edge-case complexity and makes offline-capable apps practical beyond demos.

· 23 min read

image-20251227001921810

Scheduled post: Put it off and read it on Dec 31!

Good Reads

2025-12-24 Nobody knows how large software products work { www.seangoedecke.com }

image-20251224141056474

Big software products become hard to understand because growth usually means adding options that widen the audience: enterprise controls, compliance, localization, billing variants, trials, and many special cases.

Each new option changes the meaning of existing features, so the overall behavior becomes a mesh of conditions and exceptions rather than a single clear rule set.

At that scale, many questions cannot be answered from memory or docs; the practical source of truth is often the code, plus experiments to see what happens in real environments.

Keeping complete, accurate documentation is usually infeasible because the system changes faster than teams can write, review, and maintain descriptions of every interaction.

A lot of behavior is not explicitly designed in one place; it emerges from many local decisions and defaults interacting, so "documenting it" often means discovering it.

Because understanding decays when ownership changes or people leave, organizations repeatedly pay the cost of re-investigation, and engineers who can reliably trace and explain behavior become disproportionately valuable.

~~ GPT 5.2 brainstorming ~~


Treat "how does it work" questions as first-class work, not as interruptions. Make a visible queue for them, timebox investigations, and record the answer where future readers will actually look (runbook, ownership doc, or an internal Q and A page tied to the repo). If you do not create a home for answers, the organization will keep paying the same investigation cost.

Set explicit boundaries for complexity before you add more surface area. When someone proposes a new audience-expanding capability (trial variants, enterprise policy, compliance mode, region-specific behavior), require them to also name the ownership model, the invariant it must not break, and the cross-cutting areas it touches:

  • authz
  • billing
  • data retention
  • permissions
  • UI,
  • APIs

If that extra work cannot be staffed, defer the feature or narrow it.

Prefer designs that localize rules rather than sprinkling conditions everywhere. Centralize entitlement and policy decisions behind a small number of well-named interfaces and treat them as critical infrastructure with tests and monitoring. Avoid copying "if customer has X then Y" logic across services, UI layers, and scripts, because that is how the system turns into an untraceable tangle.

Localize rules means keep one decision in one place, not duplicated across the product.

Make "explainability" a design requirement. Every major user-visible decision should have a reason code that can be logged, surfaced to support, and traced back to a single decision point. This turns investigations from archaeology into lookup: you can answer "why did it deny access" by reading the reason and following a link.

Explainability means the system can tell you why it made a decision.

Invest in a small set of canonical sources of truth, and be ruthless about what is not canonical. For example, code and automated tests are canonical; a wiki page is not unless it is owned, reviewed, and updated with changes. When people ask for "documentation", decide whether you need durable truth (tests, typed schemas, contract checks) or just a short-lived explanation (a note in an incident channel).

Use tests to document the behavior you care about, not everything. Write high-level contract tests for the most expensive-to-relearn areas: entitlements, billing state transitions, permissions, data deletion, compliance modes, and migrations. The goal is not more test coverage; it is protecting the system against silent drift in the places where drift creates confusion and risk.

Contract test means a test that asserts the externally visible behavior stays the same.

Turn emergent behavior into intentional behavior where it matters. If something "just happens" because of interacting defaults, and customers rely on it, promote it to an explicit rule with an owner, a test, and a clear place in the code. If it is accidental and harmful, add guardrails that prevent it from reappearing.

Manage knowledge loss by making ownership concrete and durable. Assign an accountable owner for each cross-cutting domain (authz, billing, data lifecycle, compliance), keep an oncall or escalation path, and require a handoff checklist when teams change. Reorgs will still happen, but you can avoid re-learning the same things from scratch.

Treat investigations as an engineering skill and teach it directly. Provide playbooks for reading logs, reproducing production behavior safely, tracing through services, and using feature flags to isolate paths. Review "investigation writeups" the same way you review code: what evidence was used, what was ruled out, and what changed in the system to prevent recurrence.

Investigation playbook means a repeatable method for finding the real cause of behavior.

Instrument the product so answers come from data instead of people. Log decision points with stable identifiers, record key inputs (tenant type, plan, flags, region, policy), and make those logs easy to query. When you can reconstruct the decision path from telemetry, you reduce dependence on tribal knowledge.

Keep the number of variants smaller than it wants to be. Standardize on a limited set of plan types, policy knobs, and deployment modes, and aggressively retire rarely used branches. If you cannot delete variants, you should at least measure their usage and cost so the business can see the tradeoff.

Create a "complexity budget" tied to revenue impact. For each cross-cutting feature, estimate ongoing cost (support, incidents, engineering time, cognitive load) and compare it to the expected value. This makes complexity a managed resource rather than an untracked byproduct of ambition.

When documentation is necessary, make it executable or tightly coupled to change. Put key behavior in schemas, config definitions, and code comments that are enforced by review. For narrative docs, use ownership, review gates, and small scope: short runbooks for common questions beat long encyclopedias that rot.

Make support and engineering share the same debugging artifacts. Provide a way for support to capture a "decision trace" (inputs and reason codes) that engineering can replay. This reduces back-and-forth and prevents engineers from having to start every investigation by reconstructing the scenario.

Finally, accept that some ambiguity is structural, and optimize for fast, reliable rediscovery. If you cannot fully prevent the fog, build systems that let you cut through it quickly: centralized decision logic, reason codes, strong observability, and a habit of writing down answers where they will be reused.

2025-12-20 abseil / Performance Hints { abseil.io }

image-20251219212548654

2025-12-13 AI Can Write Your Code. It Cant Do Your Job. Terrible Software ✨ { terriblesoftware.org } ✨

image-20251212221823279

If you’re reading this, you’re already thinking about this stuff. That puts you ahead. Here’s how to stay there:

  1. Get hands-on with AI tools. Learn what they’re actually useful for. Figure out where they save you time and where they waste it. The engineers who are doing this now will be ahead.
  2. Practice the non-programming parts. Judgment, trade-offs, understanding requirements, communicating with stakeholders. These skills matter more now, not less.
  3. Build things end-to-end. The more you understand the full picture, from requirements to deployment to maintenance, the harder you are to replace.
  4. Document your impact, not your output. Frame your work in terms of problems solved, not lines of code written.
  5. Stay curious, not defensive. The engineers who will struggle are the ones who see AI as a threat to defend against rather than a tool to master.

The shape of the work is changing: some tasks that used to take hours now take minutes, some skills matter less, others more.

But different isn’t dead. The engineers who will thrive understand that their value was never in the typing, but in the thinking, in knowing which problems to solve, in making the right trade-offs, in shipping software that actually helps people.

OpenAI and Anthropic could build their own tools. They have the best AI in the world. Instead, they’re spending billions on engineers. That should tell you something.

2025-12-07 The Math of Why You Can't Focus at Work | Off by One { justoffbyone.com }

image-20251207134426233

found in: Leadership in Tech Why you can't focus at work

image-20251207134528777

2025-11-28 How good engineers write bad code at big companies { www.seangoedecke.com }

image-20251128133307489


Bad code at large tech companies emerges not from weak engineers but from structural pressures like constant team churn, tight deadlines, and unfamiliar legacy systems that force competent people to write quick, imperfect fixes.

Short engineer tenure combined with decade-old codebases means most changes are made by relative beginners who lack deep context, making messy or fragile solutions an inevitable byproduct of the environment.

Code quality depends heavily on a small group of overloaded experts who informally guard the system, but because companies rarely reward long-term ownership, their influence is fragile and inconsistently applied.

Big tech intentionally optimizes for organizational legibility (Seeing like a software company) and engineer fungibility over deep expertise, creating conditions where hacky code is a predictable side effect of easy reassignability and constant reprioritization.

Individual engineers have limited power to counteract these structural forces, so the most effective strategy is to become an "old hand" who selectively blocks high-risk decisions while accepting that not all ugliness is worth fighting.

Most big-company work is impure engineering driven by business constraints rather than technical elegance, so ugly-but-working code is often the rational outcome rather than a failure of ability or care.

Public ridicule of bad big-company code usually misattributes blame to an individual rather than recognizing that the organization’s incentives, review bandwidth, and churn make such outcomes routine.

Improving quality meaningfully requires leadership to change incentives, stabilize ownership, and prioritize expertise, because simply hiring better engineers cannot overcome a system designed around speed and reassignability.

2025-11-28 Feedback doesn't scale | Another Rodeo { another.rodeo }

When you're leading a team of five or 10 people, feedback is pretty easy. It's not even really "feedback”: you’re just talking. You may have hired everyone yourself. You might sit near them (or at least sit near them virtually). Maybe you have lunch with them regularly. You know their kids' names, their coffee preferences, and what they're reading. So when someone has a concern about the direction you're taking things, they just... tell you.

You trust them. They trust you. It's just friends talking. You know where they're coming from.

At twenty people, things begin to shift a little. You’re probably starting to build up a second layer of leadership and there are multiple teams under you, but you're still fairly close to everyone. The relationships are there, they just may be a bit weaker than before. When someone has a pointed question about your strategy, you probably mostly know their story, their perspective, and what motivates them. The context is fuzzy, but it’s still there.

Then you hit 100

image-20251127225216543

2025-11-28 Tiger Style { tigerstyle.dev }

Tiger Style is a coding philosophy focused on safety, performance, and developer experience. Inspired by the practices of TigerBeetle, it focuses on building robust, efficient, and maintainable software through disciplined engineering.

Summary

  1. Core principles
  2. Design goals
    1. Safety
    2. Performance
    3. Developer experience

image-20251127225023662

2025-11-09 To get better at technical writing, lower your expectations { www.seangoedecke.com }

image-20251108161947048


Write for people who will not really read you. Put your main point in the first sentence and, if possible, in the title. Keep everything as short as you can. Drop most nuance and background. Say one clear thing for a broad audience, like "this is hard" or "this is slow." Reserve long, detailed docs for the tiny group of engineers who actually need all the details. Before you write, force yourself to express your idea in one or two sharp sentences, and build only the minimum around that.

Do this because almost nobody will give your writing full attention. Most readers will glance at the first line, skim a bit, then stop. They do not share your context, they do not care as much as you do, and they do not have time. No document will transfer your full understanding or perfectly align everyone. Real understanding comes from working with the system itself. In that reality, a short, front-loaded note that lands a single important idea is far more useful than a long, careful essay that most people never finish.

2025-11-05 Send this article to your friend who still thinks the cloud is a good idea { rameerez.com }

image-20251104195529880

2025-11-02 Your URL Is Your State { alfy.blog }

image-20251102114702224

Couple of weeks ago when I was publishing The Hidden Cost of URL Design I needed to add SQL syntax highlighting. I headed to PrismJS website trying to remember if it should be added as a plugin or what. I was overwhelmed with the amount of options in the download page so I headed back to my code. I checked the file for PrismJS and at the top of the file, I found a comment containing a URL:

/* https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+css-extras+markdown+scss+sql&plugins=line-highlight+line-numbers+autolinker */

I had completely forgotten about this. I clicked the URL, and it was the PrismJS download page with every checkbox, dropdown, and option pre-selected to match my exact configuration. Themes chosen. Languages selected. Plugins enabled. Everything, perfectly reconstructed from that single URL.

It was one of those moments where something you once knew suddenly clicks again with fresh significance. Here was a URL doing far more than just pointing to a page. It was storing state, encoding intent, and making my entire setup shareable and recoverable. No database. No cookies. No localStorage. Just a URL.

This is scary:

image-20251102120457125

2025-11-04 hwayne/awesome-cold-showers: For when people get too hyped up about things { github.com }

image-20251103200937106

🪻 Bloom Filter

2025-11-25 Bloom filters: the niche trick behind a 16× faster API | Blog | incident.io { incident.io }

image-20251124210734023

The goal of the optimization is to reduce the cost of fetching and decoding data by pushing as much of the filtering as possible into Postgres itself, specifically by making better use of the JSONB attribute data so that fewer irrelevant rows ever reach the application.

Two approaches are considered: using a GIN index on the JSONB column, which is the standard Postgres solution for complex types, or introducing a custom encoding where attribute values are turned into bit strings so the database can perform fast bitwise membership checks instead.

A bloom filter is introduced as the core idea of the second approach: a probabilistic data structure that can say an item is definitely not in a set or might be in it, with the benefit of very efficient use of time and space.

A bloom filter is a compact data structure that lets you test set membership quickly by allowing some false positives but no false negatives.

2025-12-15 Bloom Filters { www.jasondavies.com }

image-20251214200242738

2025-12-15 jasondavies/bloomfilter.js: JavaScript bloom filter using FNV for fast hashing { github.com }

FNV Hash

2025-12-15 lcn2/fnv: FNV hash tools { github.com }

Fowler/Noll/Vo hash

The basis of this hash algorithm was taken from an idea sent as reviewer comments to the IEEE POSIX P1003.2 committee by:

Phong Vo Glenn Fowler

In a subsequent ballot round Landon Curt Noll improved on their algorithm. Some people tried this hash and found that it worked rather well. In an email message to Landon, they named it the Fowler/Noll/Vo or FNV hash.

FNV hashes are designed to be fast while maintaining a low collision rate. The FNV speed allows one to quickly hash lots of data while maintaining a reasonable collision rate. See http://www.isthe.com/chongo/tech/comp/fnv/index.html for more details as well as other forms of the FNV hash. Comments, questions, bug fixes and suggestions welcome at the address given in the above URL.

😁 Fun / Retro

2025-12-09 Legacy Update: Get back online, activate, and install updates on your legacy Windows PC { legacyupdate.net }

image-20251208221839451

image-20251209182938697

2025-12-09 Legacy Update { github.com } image-20251208221932089

2025-11-29 Mac OS 9 Images < Mac OS 9 Lives { macos9lives.com }

image-20251128213821336 2025-11-29 Tiernan's Comms Closet How to Set Up Mac OS 9 on QEMU { www.tiernanotoole.ie }

👂 The Ear of AI (LLMs)

2025-12-24 Tencent-Hunyuan/AutoCodeBenchmark { github.com }

image-20251224152333225

2025-12-23 The Illustrated Transformer Jay Alammar Visualizing machine learning one concept at a time. { jalammar.github.io }

Discussions: Hacker News (65 points, 4 comments), Reddit r/MachineLearning (29 points, 3 comments) Translations: Arabic, Chinese (Simplified) 1, Chinese (Simplified) 2, French 1, French 2, Italian, Japanese, Korean, Persian, Russian, Spanish 1, Spanish 2, Vietnamese Watch: MIT’s Deep Learning State of the Art lecture referencing this post Featured in courses at Stanford, Harvard, MIT, Princeton, CMU and others

image-20251222213442305 image-20251222213511023

2025-12-23 Scaling LLMs to larger codebases - Kieran Gill { blog.kierangill.xyz }

image-20251222212757899


This was the third part of a series on LLMs in software engineering.

First we learned what LLMs and genetics have in common. (part 1) LLMs don't simply improve all facets of engineering. Understanding which areas LLMs do improve (part 2) is important for knowing how to focus our investments. (part 3)


Invest in reusable context that makes the model behave like someone who already knows your codebase, so prompts can stay focused on requirements instead of restating conventions every time.

A prompt library is reusable context you give a model so it follows your codebase conventions.

Aim for workflows where output is usable in one pass, because the main cost comes from rework when you have to repeatedly intervene and patch what it produced.

One-shotting is getting a usable solution from a model in a single attempt.

Reduce hidden complexity in the system first, because accumulated compromises make every change harder for both humans and tools, which limits automation gains.

Technical debt is accumulated design and code compromises that make future changes harder and riskier.

Structure the system into clear, independent parts with stable boundaries, so changes can be localized and the context needed for edits stays small and high-quality.

Modularity is organizing software into well-defined parts that can be understood and changed independently.

Treat quality and safety as a checking problem, not a prompting problem, and build verification into the process because instructions do not guarantee the code actually meets the intent.

Verification is the process of checking that changes are correct, safe, and meet requirements.


Aside: Example usage of a prompt library.

You are helping me build a new feature. 
Here is the relevant documentation to onboard yourself to our system:
- @prompts/How_To_Write_Views.md -- Our conventions and security practices for sanitizing inputs.
- @prompts/How_To_Write_Unit_Tests.md -- Features should come with tests. Here are docs on creating test data and writing quality tests.
- @prompts/Testing_Best_Practices.md -- How to make a test readable. When to DRY test data creation.
- @prompts/The_API_File.md -- How to find pre-existing functionality in our system.

Feature request:

Extend our scheduler to allow for bulk uploads.
- This will happen via a csv file with the format `name,start_time,end_time`.
- Times are given in ET.
- Please validate the user exists and that the start and end times don't overlap. You should also make sure there are no pre-existing events for a given row; we don't want duplicates.
- I recommend by starting in @server/apps/scheduler/views.py`.

Or, better yet, the preamble is preloaded into the models context (for example, by using CLAUDE.md).

Your prompt should be thorough enough to guide the LLM to the right choices. But verification is required. Read every line of generated code. Just because you told an LLM to sanitize inputs, doesn't mean it actually did.

2025-12-09 Has the cost of building software just dropped 90%? - Martin Alderson { martinalderson.com }

image-20251208220102525


Domain knowledge is the only moat

So where does that leave us? Right now there is still enormous value in having a human 'babysit' the agent - checking its work, suggesting the approach and shortcutting bad approaches. Pure YOLO vibe coding ends up in a total mess very quickly, but with a human in the loop I think you can build incredibly good quality software, very quickly.

This then allows developers who really master this technology to be hugely effective at solving business problems. Their domain and industry knowledge becomes a huge lever - knowing the best architectural decisions for a project, knowing which framework to use and which libraries work best.

Layer on understanding of the business domain and it does genuinely feel like the mythical 10x engineer is here. Equally, the pairing of a business domain expert with a motivated developer and these tools becomes an incredibly powerful combination, and something I think we'll see becoming quite common - instead of a 'squad' of a business specialist and a set of developers, we'll see a far tighter pairing of a couple of people.

This combination allows you to iterate incredibly quickly, and software becomes almost disposable - if the direction is bad, then throw it away and start again, using those learnings. This takes a fairly large mindset shift, but the hard work is the conceptual thinking, not the typing.

2025-12-09 AI should only run as fast as we can catch up Higashi.blog { higashi.blog }

image-20251208220410815

2025-12-02 Codex, Opus, Gemini try to build Counter Strike { www.instantdb.com }

image-20251201222631428

image-20251201222543416

2025-11-29 So you wanna build a local RAG? { blog.yakkomajuri.com }

When we launched Skald, we wanted it to not only be self-hostable, but also for one to be able to run it without sending any data to third-parties.

With LLMs getting better and better, privacy-sensitive organizations shouldn't have to choose between being left behind by not accessing frontier models and doing away with their committment to (or legal requirement for) data privacy.

So here's what we did to support this use case and also some benchmarks comparing performance when using proprietary APIs vs self-hosted open-source tech.

image-20251128215551615

2025-11-28 InstaVM - Secure Code Execution Platform { instavm.io }

image-20251128135958931

LLMs perform more reliably when you avoid sending redundant or unchanged context. LLMs handle exact or brittle tasks poorly, so shift precision work into generated code or external tools. Long prompts degrade accuracy, making it essential to keep context well below the model`s limits. Models struggle with obscure or rapidly evolving topics unless you supply up-to-date, targeted documentation. AI-generated code remains fallible, requiring disciplined human review to prevent security and correctness issues.

2025-11-27 addyosmani/gemini-cli-tips: Gemini CLI Tips and Tricks { github.com }

image-20251126202046041 This guide covers ~30 pro-tips for effectively using Gemini CLI for agentic coding

Gemini CLI is an open-source AI assistant that brings the power of Google's Gemini model directly into your [terminal](https://www.philschmid.de/gemini-cli-cheatsheet#:~:text=The Gemini CLI is an,via a Gemini API key). It functions as a conversational, "agentic" command-line tool - meaning it can reason about your requests, choose tools (like running shell commands or editing files), and execute multi-step plans to help with your development [workflow](https://cloud.google.com/blog/topics/developers-practitioners/agent-factory-recap-deep-dive-into-gemini-cli-with-taylor-mullen#:~:text=The Gemini CLI is,understanding of the developer workflow).

In practical terms, Gemini CLI acts like a supercharged pair programmer and command-line assistant. It excels at coding tasks, debugging, content generation, and even system automation, all through natural language prompts. Before diving into pro tips, let's quickly recap how to set up Gemini CLI and get it running.

2025-11-15 Anthropic admits that MCP sucks - YouTube { www.youtube.com }

image-20251114190038807

image-20251114193003068

2025-11-15 Code execution with MCP: building more efficient AI agents \ Anthropic { www.anthropic.com } The core problem is context bloat. MCP clients typically load all tool definitions into the system prompt, then run multi step flows like gdrive.find_document followed by gdrive.get_document, then another tool, and so on. Every tool definition and every intermediate result lives in the context window, so each new tool call resends the whole history as input tokens. This quickly explodes into hundreds of thousands of tokens, increases latency, raises costs, and raises the chance of mistakes. Real world setups like Trey show agents with dozens of tools, including irrelevant ones like Supabase for users who do not even use it, which only adds noise.

Anthropic’s proposed fix is to treat MCP servers as code APIs and let the model write code that calls them from a sandboxed execution environment, usually using TypeScript. Tools become normal functions in a file tree, and the model discovers and imports only what it needs. Most of the heavy lifting happens in code, not in the LLM context. That cuts token usage dramatically, makes workflows faster, and lets the model leverage what it is actually good at, which is writing and navigating code rather than juggling hundreds of tool definitions and transcripts.

This code first approach also solves privacy, composition, and state in a more normal way. Large documents, big tables, and joined data can be filtered, aggregated, and matched in code, then only the small, relevant results are sent back to the model. Sensitive fields can be tokenized before they ever reach the LLM and untokenized only when communicating between backends like Google Sheets and Salesforce. State can live in memory or in files, and reusable workflows can be saved as functions or skills, which starts to look a lot like conventional SDKs and libraries.