# Keep product knowledge close to the code

> Using Elixir documentation to preserve known limitations and the product processes closest to the code.

Published: 2026-09-25T09:09:31+03:00
Canonical: https://ryanzidago.com/posts/keep-product-knowledge-close-to-the-code/

I realised this week that documentation next to the code can explain more than what a module or function does.

It can also preserve known limitations that we have deliberately decided not to address yet, and the product processes that future changes to the code must follow.

## Turn known limitations into executable documentation

Imagine a hotel booking application that lets receptionists search for a guest
by name.

The search ignores differences between uppercase and lowercase letters, but it
does not ignore accents. Searching for `Jose` therefore fails to find a guest
named `José`.

This does cause occasional problems, but receptionists can still find the
booking using its reference. The team has decided that fixing more common
search problems is currently a higher priority.

Instead of leaving that decision in a ticket that will eventually disappear from view, we can document the limitation on the function itself:

```elixir
defmodule HotelBookingApp.GuestSearch do
  @doc """
  Checks whether a guest's name matches a search query.

  Matching is case-insensitive:

      iex> HotelBookingApp.GuestSearch.matches_name?("José Silva", "SILVA")
      true

  ## Known limitation: searching without accents

  Receptionists should be able to find guests without typing accents.
  However, searching for "Jose" currently fails to match "José":

      iex> HotelBookingApp.GuestSearch.matches_name?("José Silva", "Jose")
      false

  We have deferred fixing this because reported cases are infrequent,
  and receptionists can find the booking using its reference instead.

  The example above records the current bug. Once accent-insensitive
  matching is implemented, its expected result should become `true`.
  """
  @spec matches_name?(String.t(), String.t()) :: boolean()
  def matches_name?(name, query) do
    String.contains?(
      String.downcase(name),
      String.downcase(query)
    )
  end
end
```

We also need a small test module to make the examples executable:

```elixir
defmodule HotelBookingApp.GuestSearchTest do
  use ExUnit.Case, async: true

  doctest HotelBookingApp.GuestSearch
end
```

This is a simple bug and a simple example, but you can extrapolate it to the
complexity you deal with every day in your own application and domain.

The doctest does more than describe the bug.
It makes the current behaviour executable. If that behaviour changes, the
failing doctest creates a reason to revisit the documentation. It cannot verify
that the surrounding explanation remains accurate, but it does connect the
illustrated behaviour to the implementation.

This also gives coding agents and automated reviewers the context to recognise
an acknowledged limitation, potentially reducing repeated reports of an issue
we have already considered.

## Document product processes next to the code they govern

Not every piece of product knowledge can or should be executable.

Suppose a new engineer needs to add a permission to the application's permission system. The safe process depends on whether the permission protects a new feature or an existing feature that customers already use.

That knowledge belongs in the module documentation closest to the permission definitions:

```elixir
defmodule HotelBookingApp.Authorisation.Permission do
  @moduledoc """
  Defines the permissions that can be assigned to a permission role.

  ## Adding a permission

  First, determine whether the permission protects a new feature or an
  existing feature.

  For a new feature, add the permission to the list below.

  For an existing feature used by customers in production:
  - Add the permission to the list below.
  - Create a data migration that grants it to every role whose users could
    previously access the feature, preserving existing access by default.
  - If a customer requested the new restriction, confirm which of their roles
    should retain access and apply those agreed restrictions in the migration.

  The migration prevents existing users from unexpectedly losing access when
  the feature becomes permission-controlled.
  """
end
```

This is not a doctest. It is still valuable because it puts the product process
where an engineer or coding agent is most likely to need it: beside the code
they are about to change.

Doctests keep the illustrated behaviour checked against the implementation.
The surrounding explanations and product processes still need maintenance, but
placing them beside the relevant code makes them easier to discover when
someone changes it.
