# Foundation 3. Visibility

## Visibility and coupling

An API is the protocol that allows two modules to collaborate. Your contract gathers the minimum information a consumer needs to use it without knowing its implementation.

A signature can show names and types:

```text
Set.Add(Value) → AddResult
```

But it doesn't by itself explain whether duplicates are allowed, how a value is compared, what changes after the call, or what will happen when the call is repeated. These guarantees also belong to the contract.

Each visible element creates a dependency: the consumer can use it and the provider will have to keep or migrate it. Reducing visibility decreases coupling only when the API continues to express all necessary capability.

## What the consumer can observe

| Part | Question |
|---|---|
| Consumers | Who uses the API? |
| Messages | What can you ask or communicate? |
| Vocabulary | What do inputs, results, errors and events mean? |
| Validity | What values ​​and sequences are accepted? |
| Guarantees | What result, state or effect is preserved? |
| Visibility | Which consumers can access the contract? |
| Compatibility | What previous uses should still work? |

Not all APIs need to document each dimension in the same detail. The depth depends on the risk and the number of consumers. A local module with pure operation can be made clear with a signature and two examples; a shared protocol will need more precision.

## Example: a set without duplicates

Let's consider this standalone API:

```text
Set.Add(Value) → Added | AlreadyPresent
Set.Contains(Value) → bool
Set.Size() → integer
```

Your contract can be expressed with four guarantees:

1. adding a missing value returns `Added`;
2. after adding it, `Contains` returns `true`;
3. adding the same value again returns `AlreadyPresent`;
4. repeating the operation does not increase `Size`.

The contract still needs a decision on equality: when do two values ​​represent the same thing? You don't need to decide whether the implementation uses a hash table, a tree, or a list. Any of them are valid if you keep the guarantees.

This example also shows the difference between API and vocabulary. `Value`, `Added` and `AlreadyPresent` are part of the messages. There are three new APIs. They would only need independent contracts if they acquired their own behavior, consumers or evolution.

## Example in Go

This implementation protects the contract and leaves the internal structure outside the API.

```go
type AddResult uint8

const (
    Added AddResult = iota
    AlreadyPresent
)

type UniqueSet[T comparable] struct {
    values map[T]struct{}
}

func (s *UniqueSet[T]) Add(value T) AddResult {
    if s.values == nil {
        s.values = make(map[T]struct{})
    }
    if _, exists := s.values[value]; exists {
        return AlreadyPresent
    }
    s.values[value] = struct{}{}
    return Added
}

func (s *UniqueSet[T]) Contains(value T) bool {
    _, exists := s.values[value]
    return exists
}

func (s *UniqueSet[T]) Size() int {
    return len(s.values)
}
```

## Public API and implementation

A decision belongs to the API when a legitimate consumer needs to use or distinguish it and the provider is willing to keep it.

| It belongs to the contract | Remains in implementation |
|---|---|
| Intentions that the consumer can express | Internal call sequence |
| Values ​​and differences that change your behavior | Intermediate structures |
| Errors that you can act on | Technical errors already translated |
| State and observable effects | Algorithms and coordination mechanisms |
| Order or quantity when they alter the external result | Optimization and distribution of work |

A small surface area is useful if it allows full capacity to be expressed. Hiding a necessary difference does not reduce coupling: it forces the consumer to deduce it or look for it in the implementation.

## Status and effects

The immediate result is just a form of observation. A message can modify state that is then queried through the API or produce an effect directed to another module.

The contract must name these effects when the consumer depends on them. You should not publish the coordination used to obtain them.

In the set example, `Size` and `Contains` allow you to observe the state without exposing its representation. The consumer can check the warranty without receiving the internal collection or modifying it by reference.

When the API returns collections or mutable structures, you must decide who retains ownership. If they still belong to the provider, a copy or immutable view can prevent accidental changes. If ownership is transferred, it should be said explicitly.

## Repetition and idempotence

An operation is idempotent when repeating the same intention produces the same observable effect as executing it once. It does not mean that the provider executes a single statement or returns the same instance.

`Set.Add` is idempotent with respect to content: repeating the same value does not create another entry. The response can change from `Added` to `AlreadyPresent` and the guarantee will still be valid, because the final state does not change.

Idempotence only deserves to enter into the contract when there are retries, repetitions or duplicate deliveries that the consumer must be able to handle. Adding it unnecessarily introduces identity, status and retention costs.

## Compatibility

A published API accumulates consumers. Changing a name, a validity rule, an error, or the meaning of a field can break them even if the code continues to compile.

Before modifying the contract you must know:

- what consumers exist;
- what behavior they observe today;
- whether the change can be additive;
- how the two versions will coexist temporarily;
- when the previous contract can be withdrawn.

Compatibility does not require keeping a bad decision forever. It requires treating its correction as a migration and not as an internal refactor.

## Hands-on review

Before implementing an API, check:

1. The consumer and its intention are identified.
2. Each message expresses a necessary capacity.
3. Inputs, results and errors have unambiguous meaning.
4. State and observable effects are declared.
5. Internal details remain replaceable.
6. Shared values ​​have clear rules and responsibility.
7. Repetition and compatibility are decided only where they matter.

A good contract allows two things at the same time: that the consumer uses the module with confidence and that the provider changes its interior freely.
