Controlling Complexity in the API

Controlling Complexity

(In the API)

What is complexity?

The natural result of organically grown systems

Interconnectedness.

Mushrooms

Mushrooms?!

Yes. Mushrooms.

Tasty

Poisonous if it's the wrong kind

Grows on rotting stuff

Actually grows beneath the surface, popping up just when it's ripe

An example

An example

An example

Mushrooms.

If you never have to care what's in those methods, great...

... but you do have to reason about the system as a whole

Granularity

Do you operate on a given type of object field-by-field?

Lots of micro-optimizations. Small mutations are the key actions.

Or do you operate on it as a whole?

Bigger pieces. You work with the whole thing all at once.

Granularity

Field-by-field

Whole object

Granularity

Field-by-field

Whole object

Representation

Field-by-field

Everything in the object is locked up tight. Everything takes explicit conversion.

Whole object

We're working with the canonical representation.

Representation

Field-by-field

You have to extend the API to handle invalid data. What you've got inside your object is (assuming no bugs) good, but now you have to do validation before you put data in.

Whole object

You can actually load invalid data and work with it in its own domain. No need to have an intermediate representation for not-yet-validated data.

Lifecycle

Does our User object last milliseconds? Hours? Days?

In PHP, the answer is almost always 'milliseconds'. The entire object structure is torn down for each request. All state saved must be explicit.

In a system like a router's firmware, where the same code will have long-lived instances for objects like route table entries, the lifecycle is very different. Objects may live for years. Those are state objects, and any change to their internal representation affects the system as a whole.

State Transitions

Bugs happen any place the state of the system can change.

To reduce the amount of code that has to be analyzed to see a change's effect, we define points where the state can change, and enforce those.

In our user object example, the system as a whole is affected only when the object is persisted. Our PHP doesn't talk much to external systems.

State Transitions

State changes in line 3. And 4. And 6. And 7. And maybe in 8. Line 8 looks like a fantastic place to update our stored copy if we discover it's in an old format.

There be dragons.

State Transitions

State changes only when we save the object.

But how are you sure?

If this were Erlang, we'd match only valid objects. If this were OCaml, validated objects would be a different type, and we could declare that most of our functions only work with the persisted, validated state.

This is PHP. We'll have to assert things are in the right state.

The state we really care about is "is it valid?". That's it. Better to define that in one place.