Case Study 40.1 — Anatomy of a First Open-Source Contribution

"The best way to get a project done faster is to start sooner." — a maxim of every maintainer who has waited for a first-time contributor to stop being afraid of the repository.

Executive Summary

You have built a solver you understand completely, but it lives on your own disk. The single most effective way to make your competence visible — and to keep learning from people better than you — is to contribute to an open scientific code that others can watch you improve. This case study walks the whole loop of a first contribution, not as an intimidating leap but as a small, structured task: read the code's architecture the way Chapter 36 taught you; find a newcomer-sized improvement; write a small, correct, well-tested change; and verify it before you ever open a pull request. We use the fortran-lang community projects — the standard library (stdlib) and the package manager (fpm) — as the concrete example, because they are open, active, and deliberately welcoming to newcomers, but the method transfers to any scientific code.

Skills applied - Reading a large code's layout and module hierarchy — §36.1, §36.2 (Chapter 36) - Testing a change before you trust it — regression and unit tests (Chapter 37) - Writing a small, pure, fully-annotated procedure — §6.2, §6.4 (Chapter 6) - Numerical method behind the contribution — the trapezoidal rule (Chapter 22) - Career framing — a public contribution as portfolio evidence (§40.4, §40.5)

Background

Newcomers imagine that contributing to a well-known code means arriving with a brilliant, sweeping change. Maintainers know the opposite: the contributions that keep a project healthy are overwhelmingly small — a fixed typo in the documentation, a missing test that pins down existing behavior, a clearer error message, a one-line performance fix in a hot loop someone profiled. These are gifts, because they reduce the maintainers' backlog without adding risk, and the review that follows is how you absorb the project's conventions. Your goal for a first contribution is not to impress; it is to land a small correct change and, in doing so, to learn how the project works.

The fortran-lang projects are a good place to start precisely because they are engineered for this. They use fpm, so building and testing is three words; they keep their source under src/ and their tests under test/, the layout you already know; and they mark approachable tasks so a newcomer can find one. We will approach a representative task — adding a small, well-tested numerical utility — and produce exactly the kind of compilable, self-checking artifact a scientific library expects.

There is a career reason to do this beyond the learning, and it is the argument of §40.4 and §40.5 made concrete. A private repository proves competence only to you. A merged contribution to a code other people use is public, permanent, and attributable: a hiring manager can follow the link, read your change, read the review it went through, and see that people who maintain real software trusted your work. That is a stronger signal than any bullet point you could write about yourself, because you did not write it — the merge did. The whole loop below exists to produce that signal, and it starts with a change small enough that nothing about it is intimidating.

Phase 1 — Read the Architecture Before You Touch Anything

The first move with any unfamiliar code is the one from Chapter 36: do not start reading files top to bottom; find the districts. A community numerical library, viewed from orbit, has a layout you can now predict:

the-library/
├── README.md              what it is, how to build, how to contribute
├── CONTRIBUTING.md        the conventions you must follow (READ THIS FIRST)
├── LICENSE                the terms
├── fpm.toml               the build manifest
├── src/                   THE LIBRARY — one module per topic (stats, sorting, quadrature, …)
├── test/                  a test program per module — where your change must prove itself
├── doc/                   the documentation sources
└── example/               runnable usage samples

You classify each directory by its role without reading a line of the science: src/ is the library, test/ is where correctness is enforced, doc/ is the prose, and CONTRIBUTING.md is the rulebook. The single most important file for a newcomer is CONTRIBUTING.md: it tells you the coding style, whether every change needs a test (in a serious numerical library, it does), and the mechanics of submitting. Reading it first is the difference between a contribution that is merged and one that bounces on a convention you could have known.

The reading discipline, applied. Where does state live? In a well-built library, almost nowhere global — utilities are pure functions of their arguments, which is exactly why they are easy to test and safe to change. Follow the data: a quadrature routine takes samples in, returns a number out, touches nothing else. That property is what makes it a safe first target.

Phase 2 — Find a Newcomer-Sized Task

With the map in hand, look for a task that is small, self-contained, and testable. Adding a pure numerical utility to a quadrature or statistics module is close to ideal: it touches one file in src/, adds one test in test/, depends on nothing global, and has a mathematically checkable answer. Our representative task: add a composite trapezoidal-rule integrator, trapz(x, y), that integrates sampled data — a staple you met in Chapter 22.

Why this shape of task is the right first one:

Property Why it matters for a newcomer
Touches one src/ file Small diff, easy to review, low risk
pure function of its arguments No global state to reason about; safe
Mathematically checkable You can prove it correct with a known integral
Needs one test Teaches you the project's test conventions

Phase 3 — Write the Change (small, pure, documented)

Here is the contribution, written to the standard this book has taught throughout: implicit none, a single dp kind, intent on every argument, pure so the compiler and the reader can trust it, and a doc comment. It is deliberately tiny — that is the point.

module contrib_quad
  use, intrinsic :: iso_fortran_env, only: dp => real64
  implicit none
  private
  public :: dp, trapz
contains
  !> Composite trapezoidal integral of samples y taken at abscissae x.
  !> Assumes size(x) == size(y) >= 2 and x strictly increasing.
  pure function trapz(x, y) result(area)
    real(dp), intent(in) :: x(:), y(:)
    real(dp) :: area
    integer  :: i, n
    n = size(x)
    area = 0.0_dp
    do i = 1, n - 1
      area = area + 0.5_dp * (x(i+1) - x(i)) * (y(i) + y(i+1))
    end do
  end function trapz
end module contrib_quad

Every choice here is a convention a numerical library will expect and a reviewer will look for: the routine is pure, its precision is explicit, its interface is assumed-shape and fully intent-annotated, and its one assumption (increasing x, matching sizes) is documented rather than silently relied upon.

Phase 4 — Verify It Before You Trust It

A contribution to a numerical library without a test will, and should, be rejected. The discipline is from Chapter 37: pin the behavior with a case whose answer you know. Integrate $f(x) = x^2$ over sample points $0,1,2,3,4$. The trapezoidal rule will overestimate a convex function, so the test also teaches the method's character.

program test_trapz
  use contrib_quad, only: dp, trapz
  implicit none
  real(dp) :: x(5) = [0.0_dp, 1.0_dp, 2.0_dp, 3.0_dp, 4.0_dp]
  real(dp) :: y(5) = [0.0_dp, 1.0_dp, 4.0_dp, 9.0_dp, 16.0_dp]   ! y = x**2
  real(dp) :: area
  area = trapz(x, y)
  if (abs(area - 22.0_dp) < 1.0e-10_dp) then
    print '(a, f0.2, a)', 'trapz test PASS: got ', area, ', expected 22.00'
  else
    print '(a, f0.2, a)', 'trapz test FAIL: got ', area, ', expected 22.00'
  end if
end program test_trapz
$ gfortran -std=f2018 -Wall -O2 contrib_quad.f90 test_trapz.f90 -o test_trapz && ./test_trapz
trapz test PASS: got 22.00, expected 22.00

The hand computation (sanity check). With unit spacing, each trapezoid contributes $\tfrac{1}{2}(y_i + y_{i+1})$: the intervals give $\tfrac{1}{2}(0+1)=0.5$, $\tfrac{1}{2}(1+4)=2.5$, $\tfrac{1}{2}(4+9)=6.5$, $\tfrac{1}{2}(9+16)=12.5$, and $0.5 + 2.5 + 6.5 + 12.5 = 22.0$. The exact integral $\int_0^4 x^2\,dx = \tfrac{64}{3} \approx 21.33$, so the trapezoidal result of $22.0$ is a slight overestimate — precisely the behavior Chapter 22 predicts for a convex integrand, and a reassuring sign the routine is right rather than accidentally exact.

Phase 5 — Open the Conversation

Now, and only now, do you submit — following the mechanics in CONTRIBUTING.md: fork, branch, commit with a clear message, push, open a pull request that says what you changed and why, and links the test. What happens next is not an exam but a conversation: a maintainer may ask for a naming tweak, an extra edge-case test (unequal sizes, a single point), or a doc line. That review is the learning. When it merges, you have a public, permanent, linkable record — the thing §40.4 said turns private competence into visible evidence.

Step What you do Book skill
Read CONTRIBUTING.md Learn the conventions §36.1 (orient first)
Fork + branch Isolate your change Chapter 37 (git)
Write the pure routine One small, documented function Chapter 6
Add a test with a known answer Prove it correct Chapter 37
Build + run with fpm Verify locally Chapter 16
Open the PR Start the conversation §40.5

What lands, and what bounces. It helps to see the review from the maintainer's side, because their priorities are not mysterious once named. A change lands when it is small enough to review in one sitting, comes with a test that proves it, matches the project's existing style, and does one thing with a clear commit message and PR description. A change bounces — or stalls for weeks — when it is large and touches many files at once, arrives without a test, ignores a convention stated plainly in CONTRIBUTING.md, mixes several unrelated changes into one pull request, or reformats code the author did not otherwise touch (which buries the real change in noise). None of these are about talent; they are about respecting the reviewer's time and the project's stability. Every one of them is under your control on a first contribution, which is exactly why a first contribution should be small — it lets you get all of them right at once.

Discussion Questions

  1. Why is a small, pure, well-tested utility a safer and more welcome first contribution than a large change to a code's core physics? Tie your answer to "where state lives" from Chapter 36.
  2. The test asserts the answer is $22.0$, not the exact integral $21.33$. Why is asserting the trapezoidal answer (rather than the analytical one) the correct thing to test here?
  3. CONTRIBUTING.md is named the most important file for a newcomer. What kinds of rejection does reading it first prevent?
  4. How does landing this contribution function as a résumé artifact in the sense of §40.4 — what does it prove to a hiring manager that a private repository does not?

Your Turn: Extensions

  • Option A (test hardening). Add two edge-case tests to test_trapz: a two-point integral (one trapezoid) and a case with non-uniform spacing. Hand-compute both expected values first.
  • Option B (method upgrade). Write a simpson(x, y) companion for uniformly spaced data and test it on the same $x^2$ samples — Simpson's rule integrates quadratics exactly, so your test should assert $\tfrac{64}{3}$ within a tight tolerance. Compare the two methods in a sentence.
  • Option C (the real thing). Actually find an open scientific Fortran code, read its CONTRIBUTING.md, and identify one genuine newcomer task. Describe it and which chapter's skills it needs. You do not have to submit — but you should be able to.

Key Takeaways

  • A first open-source contribution is a small, correct, tested change, not a heroic rewrite — maintainers value it precisely because it adds no risk.
  • You already have every skill the loop requires: read the architecture (Chapter 36), write a clean pure routine (Chapter 6), prove it with a test against a known answer (Chapters 22, 37), build with fpm (Chapter 16).
  • CONTRIBUTING.md first, always: it encodes the conventions that decide whether your change merges.
  • A merged contribution is public, permanent evidence of competence — exactly the portfolio signal §40.4 and §40.5 said to build. The barrier is lower than it feels; the loop only gets easier.