The Role Of Affordance In Software Design

Written by fagnerbrack | Published 2017/07/11
Tech Story Tags: programming | software-engineering | api-design | software-development | javascript

TLDRvia the TL;DR App

Building software is similar as if you were crafting a real object

The picture of a gun built with the muzzle pointed towards the shooter. Not very useful I guess...

I’m very fond of War Memorials.

It's great to have the opportunity to see very closely the real items worn by the soldiers at war. Preserved throughout the times, they foster imaginations for the ones who have never experienced their usage. Artifacts, untouchable since the first time they were put inside the glass.

When walking through the World War II room of the Australian War Memorial, you can see how the quality of the equipment was very poor at that time. Imagine a soldier holding a machine gun with no laser sight or electronic apparatus, just a naive piece of iron carefully built for the main purpose to hurt a living creature, built to trigger a mechanism that will pressure the powder against the case and throw the bullet at high speed towards the pointed direction.

And that’s when you realize how formidable engineering is.

It’s formidable how a tool can be crafted for the sole purpose of being handled by a person.

Engineering is formidable because it allows crafting a tool carefully built for the sole purpose of being handled by a person.

Even though a machine gun is built for a soldier to aim and shoot, it can have more than one usage.

It can be used by anybody to try to knock down the enemy by hitting their head with the hardness of the iron. In this case, it can be used by someone who doesn’t know how to shoot, but towards the same purpose.

It can be used to open a bottle of beer by leveraging the format of the gun. In this case, it can also be used by a person who doesn’t know how to shoot, but towards a different purpose that was never thought when the gun was created.

It is said that opening a bottle, hitting someone with the iron, and shooting a bullet are the things a WWII machine gun affords a person to do.

The ability of a machine gun to provide means of usage is called Affordance.

In more generic terms:

Affordance is the possibility of an action on an object or environment.

Affordance on Wikipedia

But what do machine guns and affordance have to do with Software Development?

Affordance (or more specifically Perceived Affordance) can also be one criterion to measure the effectiveness of the code you’re writing and the API you create. An API with clear perceived affordance allows the developer to understand its purpose and to use it seamlessly inside the Cybernetic Environment it was designed for.

Let’s say you’re creating a game using object-oriented principles. It has a Soldier class which uses a MachineGun:

You can design the Soldier to use the MachineGun against the enemy. In this case, the soldier is the client consuming the MachineGun API.

Since it’s programming and not real life, you can define the allowed actions (methods) for the clients that are going to use the MachineGun:

However, just because a MachineGun has the functionality to open(bottle), that doesn't mean a Soldier is allowed to do it. And that's exactly why the Soldier class seen earlier is not consuming the open(bottle) method from the MachineGun, only the useHandEquipmentAgainst(enemy).

You can say the **MachineGun** affords the soldier to shoot if the environment allows the soldier to do so.

You can also allow the soldier to open a bottle of beer, but you need to create that functionality explicitly:

The above is a kind of affordance that can exist between the interconnected modules in this cybernetic environment we created, where the conceptual objects are all isolated from the real life and whatever an object affords their users to do can be limited by the programming language safety mechanisms — like the Java static typing system or JavaScript runtime syntax errors, for example.

However, you can take this approach of building affordable systems one step further.

If you handover an unloaded machine gun to a person from the Stone Age, the first thing they might use it is to crack the rocks. That’s the only affordance they can perceive for that object.

The same way, if you give a carefully crafted programming API MachineGun class to a programmer who doesn't know how to use in the way it was designed for, they might create a getWeapon() inside the Soldier class to use the Soldier's weapon functionalities for another purpose. Using a getter can be the only thing they know and therefore the only thing they perceive the code affords them to do:

The above is the violation of a well-documented principle called Tell, Don’t Ask and The Law Of Demeter. It’s a very common mistake when people start to add getters to retrieve information in places they are not suitable.

If programmers don’t understand the underlying purpose of the environment and the APIs they are consuming, it’s very hard to efficiently use what the tools afford them to do towards the goal of creating an efficiently designed system that is sustainable. The whole environment affords them to do things the original developer never thought of — like adding a getter — and there's not much that can be done to prevent it.

Perceived affordance also defines what a programmer can do with the code. A programmer can discover new types of affordance that were not initially designed for that code.

A soldier also needs to rely on the quality of the gun to make sure it can be used efficiently. For example: when shooting, the gun should throw a bullet at high speed towards the target, not towards the shooter.

A programmer also needs to rely on the quality of the API to make sure it can be used efficiently. If Soldier has a method equipShootingGun(new HandGun()) and equipWhiteGun(new Knife()), a client of the Soldier class that calls the .stab() method would expect the Soldier to use the Knife, not the HandGun to execute the action:

The example above is a violation of the Principle Of Least Astonishment. When calling the .stab() method, the Soldier is shooting. That's an astonishing behavior from the point of view of the clients running the Soldier instance.

The designer of the API is the one responsible for building what the API does explicitly and implicitly. If they don’t care about that, the system will eventually behave in a way that is different from what it was intended for once it starts to grow.

Don't underestimate a programmer's ability to find creative ways to consume an API.

The API design defines what the code affords a programmer to do. If the designer built the API incorrectly then it will be harder to consume the components of the system in a predictable manner.

In real life, it’s very easy for a badly designed machine gun to afford the soldier to shoot them in the foot. That failure in the design can take a long time to be noticed by those using the weapon.

Same goes for software.

Most people just build it to work and don’t care about design. There’s nothing wrong with that as long as the project is a harmless experiment or an exploratory coding. If you intend to build something scalable, then you need to think about design and mostly about the things your API and code affords a consumer to do.

There are some things you can’t change, like the mistakes from the Tell, Don’t Ask principle or the programming language rules. However, thinking about these concepts is at least one step in the direction we should all be aiming for.

Engineering in real life and in software design share the same fundamentals.

There are perceived affordances for what you consume and what you build, you just need to recognize them and analyze their tradeoffs. But be aware they might be different for each person.

An API also requires manufacturing skills from the API designer for it to behave correctly, same for the designers of the machine gun.

All of these principles might help you to not build a gun that will eventually shoot somebody else in the foot.

And you, what does your code affords another developer to do?

Thanks for reading. If you have some feedback, reach out to me on Twitter, Facebook or Github.


Published by HackerNoon on 2017/07/11