Effective ML: Errors, Abstraction and Managing Complexity

A Jane Street engineer discusses explicit failure, boilerplate, type-system restraint and the practical role of side effects.

채널
Jane Street
게시일
2016년 3월 31일
영상
23:58
읽기
5분
원문
영어
YouTube에서 열기
스크롤하면 영상이 오른쪽 아래로 작아져 계속 재생됩니다. 자막은 읽기 언어를 따릅니다.
읽기 언어2개 언어로 읽을 수 있음
이 글은 아직 한국어 번역이 없어 영어 원문으로 표시합니다.
요약

30초 요약

In the second part of an Effective ML lecture, the speaker argues that readable, robust software makes failure explicit and abstracts repeated patterns without introducing unnecessary complexity. He also discusses mutation, code review, team size, human involvement in trading and the historical choice of OCaml.

  1. 1Options and exceptionsA search can return an option or provide an explicitly named exception-raising variant, depending on caller needs.
  2. 2Boilerplate hides changeRepeated code is hard to update consistently and tedious to review.
  3. 3Complexity is the enemyA sophisticated type-level invariant is not helpful if it makes the interface incomprehensible.
  4. 4Mutation as communicationShared mutable state links parts of a program and can make local reasoning harder.
  5. 5Reviewing critical codeThe speaker describes multiple reviewers and tests for the trading system’s critical path.

핵심 정리

  1. Expose expected failure in types when callers need to handle it, and name exception-raising alternatives clearly.
  2. Abstract repeated patterns so common behavior and meaningful variations are visible.
  3. More static guarantees are not automatically better if they make interfaces too complex to understand.
  4. Avoid unnecessary mutation while recognizing that useful programs need side effects.
  5. Code review, tests and small coherent projects support dependable systems.
  6. Language choices depend on the performance and operational needs of the application.
전문

챕터별로 다듬은 전문 읽기

원본 자막만 보기
01

Make errors explicit

The speaker contrasts option-returning interfaces and exceptions. He explains why error-handling choices depend on the consequences of failure.

A key idea in effective ML programming is to make common errors in called functions clear and explicit. The familiar advice to use exceptions for exceptional circumstances reflects two concerns: exceptions create a nonlocal control flow, and implicit error handling makes it easy not to think about failure.

I do not argue against exceptions altogether; they are useful. But whether a failure is exceptional depends on the caller’s context. A list search might be expected always to find an element in one piece of code, while another caller genuinely does not know whether it exists.

A list interface can offer both find, which returns an option, and find_exn, which may raise an exception. The former makes absence visible in the type and forces the caller to handle it; the latter makes the possible exception visible in its name. Our convention uses the _exn suffix for operations that can commonly fail on their inputs.

Usually the option-returning version is preferable because the type system carries the possibility of failure rather than relying solely on a naming convention. We learned this through experience and revised much of our code in this style to improve robustness.

Context matters to error-handling design. Stephen Weeks, author of the MLton compiler, observed a change in perspective when moving to Jane Street: a compiler that exits with an exception and stack trace is inconvenient, but a trading system that dies in the middle of the day is a much more serious problem.

Some interfaces avoid the problem entirely. A fold can operate on an empty list, unlike an operation that requires a head or a successful search. A function with simple error behavior is easier to use confidently, while explicit failure conditions make the remaining cases easier to reason about.

02

Avoid boilerplate

Repeated patterns can hide mistakes and become hard to update. ML abstractions can make the common pattern and its variations explicit.

Next, avoid boilerplate: similar code copied repeatedly with small but important changes. Even good programmers use it when the language does not provide an easy abstraction for the pattern, or when they do not know how to express that abstraction.

Repetition creates two problems. A pattern evolves, and developers may miss one of its many copies when changing it. Repetitive code is also dull to review; people examine the first instance carefully and can overlook a significant difference in later copies.

C provides relatively limited abstraction tools for this kind of pattern, leaving macros or copying as common choices. ML offers functors and higher-order functions. I suggest a low tolerance for duplication: even two copies are a reason to look for an abstraction.

An abstraction can be worthwhile even when it makes the code longer. A local function states the common pattern once, and its arguments show what varies. The resulting code can be more explicit and easier to follow than several nearly identical copies.

03

Control complexity

Type-level guarantees and purity are valuable, but excessive sophistication can make code harder to understand. The goal is a practical balance.

Powerful type systems create the opposite temptation: encoding increasingly intricate invariants because it is fun and possible. I have seen interfaces that experienced ML programmers cannot understand even after explanation. At that point the extra static guarantee may have increased overall complexity rather than helping.

The central challenge in software engineering is managing complexity. Learn clever techniques and use them where they help, but exercise restraint. Code must fit the minds of the people who will maintain it, not merely an elegant abstract model.

Side effects and mutation deserve similar balance. Prefer code without unnecessary mutation because a shared reference is a communication channel between different parts of a program. Such channels connect distant code and make it harder to understand a component in isolation.

Do not turn purity into a religion. Useful programs interact with the outside world: reading a socket, writing a file or checking the clock is a side effect. Haskell disciplines the separation between pure and effectful code; it does not eliminate effects from useful programs.

Pushing effects outward can help, but not if the result becomes more complicated than a straightforward imperative solution. Sometimes an imperative algorithm is easier to understand or reaches good performance without elaborate machinery. The aim is the simplest system that serves the task.

04

Questions on engineering and trading

The discussion covers review, small teams and the role of people in trading systems. The speaker closes with the historical choice of OCaml.

Asked about review, I explain that Jane Street treats code between a trading decision and the market as a critical path. Multiple people—at least two, usually three—are assigned to read changes before production, and an internal system tracks that review. Unit and regression tests also matter, but review is central to keeping the systems safe and coherent.

We try to organize development into coherent projects with small groups, roughly one to six people at the outside. Small, relatively independent pieces can fit in individuals’ heads and fit together into a larger system. I would find a large group working directly on one piece of infrastructure difficult to manage.

Asked about automation, I say the business varies widely. Some work involves people negotiating by phone, while other systems run for hours with humans watching rather than continually changing them. Most systems involve substantial human interaction; the firm is not simply a machine that runs unattended and produces money.

Trading is a game involving someone on the other side of every transaction. A model of the market must account for that other participant. Human attention, from both traders and technologists, is a central part of the integrated human and technical system.

Jane Street had used OCaml for about nine years at the time of this talk, after earlier systems using Excel, Java and C#. The choice was partly contingent on my familiarity with the language. OCaml also offered a useful balance: declarative code with a comparatively straightforward, efficient compilation strategy.

One reason we did not choose Haskell was that its performance, especially space usage, was harder to reason about for our needs at the time. Its implementation has improved, but the effort needed to make it efficient adds complexity. Language choice depends on the requirements of the system being built.

같은 채널의 다른 글

슬라이드 6장15:33EN
JSJane Street· 2026년 10월 26일Figgie Strategy: Reading Hands, Winning Majorities and Pricing CardsBradley, Mike · Jane StreetJane Street traders Bradley and Mike analyze three games to show how private information, market competition and timing affect card prices.Decision MakingMathematicsFintech
1:48:29EN
JSJane Street· 2026년 10월 16일How AI Is Changing Programming: John Krazit and Ron MinskyRon Minsky, John Krazit · Jane StreetJane Street’s AI tooling lead and the Signals and Threads host examine agent infrastructure, security, code review and the changing experience of writing software.코딩 에이전트Developer productivity평가(Evals)+2
1:36:37EN
JSJane Street· 2026년 9월 23일Aaron Bauer on Teaching Software Engineering in the Age of AIRon Minsky, Aaron Bauer · Jane StreetHow learning goals, active practice and editor design shape engineering education when models can generate the code.AI in EducationSoftware engineeringEducation+2
슬라이드 9장1:05:45EN
JSJane Street· 2026년 9월 11일Cyrus Omar on Live Programming and a Computational Commons for Humans and AICyrus OmarA demonstration of Hazel shows how immediate feedback, structured collaboration and carefully scoped permissions could support people and AI agents working together.Programming LanguagesHuman-AI Collaboration개발 워크플로와 테스트+2
1:25:00EN
JSJane Street· 2026년 9월 2일Eric Mannes on Turning Messy External Data Into Trading ResearchRon, Eric Mannes · Jane StreetFrom commodity futures and an Excel risk system to point-in-time datasets and AI, Eric Mannes explains the domain knowledge behind data engineering at Jane Street.Data quality & curation개발 워크플로와 테스트Investing+2
슬라이드 7장1:16:29EN
JSJane Street· 2026년 8월 26일Grant Sanderson and Alok Puranik: Positional Encodings Through Group TheoryGrant Sanderson, Alok Puranik · 3Blue1BrownStarting with assumptions about attention, they derive matrix exponentials and connect rotations, decay and Jordan blocks to RoPE and ALiBi.Transformers & attentionMathematics머신러닝 기초+1