DEV Community

Cover image for The Duck Tales
Anton Korzunov
Anton Korzunov

Posted on • Updated on

The Duck Tales

Once upon a time I was thinking about all the stupid things and other shenanigans I've done throughout my life. Things, I never wanted to create, things I never wanted to rewrite, and things I wanted to never see again. About thoughtfulness and a bit of wisdom.

4 things to keep in mind:

  • 🦆: If it walks like a duck and it quacks like a duck, then it must be a duck JavaScript Duck-Typing.
  • 🙈🙉: Our world is experienced differently by blind or deaf people, but it's still the same world, just different perspectives - 6 vs 9, as well as blind men and an elephant
  • 👁: An observable system is one whose internal state can be deeply understood just by observing its outputs.
  • 🔨: if all you have is a hammer, everything looks like a nail - Law of the instrument

Which was originally known as "Give a boy a hammer and chisel; show him how to use them; at once he begins to hack the doorposts, to take off the corners of shutter and window frames, until you teach him a better use for them, and how to 👉keep his activity within bounds👈."

And I am going to give you a really BIG HAMMER. Believe me or not, you can use it to hammer literally any problem.


  • church bells ringing.
  • the appearance of a man in a tuxedo with a woman in a flowing white gown.
  • rice flying through the air.

🙀 sounds like a wedding! // The theory behind Complex event processing.


There is a little problem with React and Redux state. And the idea of State in general - it's not necessarily a state, that's just a collection of some values - a few key/value pairs just met in one place and formed some data. Dead data. Impossible data.

Confused? Well, "confused" is your state. My words have changed something inside you, and the result can be observed as a final emotional state on your face – 😕

But - if you walks like a confused and you quacks like a confused, then you are a confused. And everything else is implementation details 😟

That is a core idea of Duck Typing and this idea will save your life. Already saved, maybe even multiple times, because Ducking – is what doctors do. 

It's called a triage – an attempt to understand the patient's state, the major problems and the minor problems, to save their lives. Just by looking at them, looking at how they walk and how they quack. Hopefully just looking first, and using surgery to get guts out only if necessary.

Triage is the process of determining the priority of patients' treatments based on the severity of their condition - Wikipedia

In short, when a patient comes – nurse checks:
1) the patient is yet alive
2) is not actively dying
3) and has a medical insurance
No insurance? Then the patient is probably healthy.

If it walks like a 🤒, and quacks like a 🤧 – it's 😷! Cut a leg!

Just remember the last time you went to the GP - they asked you some questions, measured your temperature and pressure, and came up with some conclusion about your health problem - like drink more water. Next!

Usually, there are enough indicators to intent the overall system state - is it actually in a "healthy" state(it does not matter how exactly) or it's in an "unhealthy" state(does not matter how exactly).

The goal of triage is not to heal💉, but plan the next step.

Signs, Signs are everywhere! Just read them! And dont forget to left the breadcrumbs, that’s called a good UX. And rice flying through the air.

Signs are everywhere

In the same way doctors are able to understand what’s wrong with you JavaScript is able to understand why undefined is not a function. Well, what is a function? What is an object? What is an array?

if something has .push and .forEach - then it could be an Array. Or, at least, something Iteratable. 👉 it doesn't have to be an Array - it should 🦆 as Array.

But there is one moment - a thing could contain many different properties, implementing different interfaces, like IDuck, IQuacker as well as IEggLayer, and duck like a pack.
This makes things a bit complicated... to explain...

What I am looking for?

I personally prefer to call a principle behind this question a WTF principle - like you are displaying your cool stuff to a friend, manager, investor or QA engineer, and they are asking you: "_So, ok that was cool, but could you please explain 👉WTF is this👈?".

Belive the old pirate - this is exactly how all your friends are thinking about your new creation.

You may replay in a short and sound manner - 🤬, but you know, there should be a better way to answer, and a bit more polite.
And probably your friend, manager, investor or QA/QE need different responses.

It's actually very hard to predict what these different people might want to hear from you, it's better and much easier to understand what they need to hear. To do this let's just imagine that they are in the same situation as you, and would have to talk about "your stuff" with someone else. Then - let's go and find the top of this food chain:

  • 💰CEO: Hey, I need you to build a new feature. I don't care how you will do it, I just trust you.
  • 😎Director: Hey, I need you to build a new feature. I don't care how you will do it, I just trust you.
  • 😸Manager: Hey, We need you to build a new feature. I don't care how you will do it, I just trust you.
  • 👻Quality: Hey, I do care how you will do, not what. And I just don't trust you.
  • 🤖Developer: 👉Hey, I've built a cool stuff👈

There is an expectation of Trust going from the top to the bottom, and no one is actually interested in the implementation details... so...

So the expectations for the backflow are the same - something they can trust, and nobody interested how you actually did it - that's implementation details, not the product itself. And you were asked to create a product 😉.

And "trust" means - your work shall meet some expectations, probably unique for every level.

If it walks like our acceptance criteria, and quacks like what we asked for – it's our feature! 🚀Ship it immediately🚀!

This is something you shall keep in mind -

Duck typing

As I said above - a single thing could quack like a pack of ducks. Let's ask every 🦆 in that pack, how it quacks about a Good Product:

  • 💰bussiness: It should be profitable. My Your salary depends on it.
  • 🤖developer: It should be maintainable.
  • 🚀performance: Loading time can't be more than 2 seconds.
  • 🕸network: latency below 300ms.
  • 🕵️‍♀️QA: code coverage above 95%.
  • 👩‍🎤design: all margins should be the same.
  • 🚔GDPR: Ha-ha! Gotcha!
  • 👂a11y: don't forget about us
  • 🤖another developer: And it still should be fun!
  • 🕵️‍♀️QA: me again. Here are 20 testing notes you should check your PR against, which is basically everything above. You're welcome, enjoy your day.

Yep, QA/QEs are duck typing their expectation since forever.

Long story short - if Product wants to be a Good Product it should meet different and parallel expectations. For every case, there should be a simple acceptance criteria, and it should be clear how to meet these requirements.
One by one.


To better understand this moment, let's take a step back, and refer to the ancient knowledge. To the Blind men and an elephant

Blind Monks and an Elephant

A group of blind men heard that a strange animal, called an elephant, had been brought to the town, but none of them were aware of its shape and form. Out of curiosity, they said: "We must inspect and know it by touch, of which we are capable". So, they sought it out, and when they found it they groped about it.

  • In the case of the first person, whose hand landed on the trunk, said "This being is like a thick snake".
  • For another one whose hand reached its ear, it seemed like a kind of fan.
  • As for another person, whose hand was upon its leg, said, the elephant is a pillar like a tree-trunk.
  • The blind man who placed his hand upon its side said the elephant, "is a wall".
  • Another who felt its tail, described it as a rope.
  • The last felt its tusk, stating the elephant is that which is hard, smooth and like a spear.

In some versions, the blind men then discover their disagreements, suspect the others to be not telling the truth and come to blows.
In some versions, they stop arguing, start listening and collaborate to "see" the full elephant.
In another, a sighted man enters the parable and describes the entire elephant from various perspectives, the blind men then learn that they were all partially correct and partially wrong.

👉 🦆 are the blind men and your application is an Elephant 👈

You might try to describe it in various ways, and then find a whole object, matching all expectations at once.


In big companies, there are different ceremonies, patterns, protocols and standards you have to meet to launch a new product. You have to pass audits and reviews, but that is... so boring.

There should be a better way to understand what shall be done, and thus let's recall another piece of ancient knowledge, recall what a classic said (Lev Tolstoy, 1828):

All happy families resemble each other,
each unhappy family is unhappy in its own way.

In other words: happy families share a common set of attributes which lead to happiness, while any of a variety of attributes can cause an unhappy family.

This concept, known as Anna Karenina principle, is quite fundamental and explain a lot - from animal domestication to PayPal (🤷‍♂️ according to Wikipedia)

And you know - All happy families resemble each other, as well as All Little Black Rain Clouds resemble each other. There is no difference.
They are all black, rainy, and not as small as that bear. Oh, that's a bear!!!

Pooh the Little Black Rain Cloud

And again, it's a subtype of Duck Typing.

We will return to the little+black+rain+clouds later, as well as check why the green great dragon cannot exist...

Behaviour

Let's try to formalize Duck Typing for some Application. Should be pretty straightforward (if not - we are doing something wrong):

  • Let's imagine you are a QE, and I am demoing my stuff to you
  • Then, when I press that button
  • Something magical should happen (and not an explosion)

Not quite formal? What about this:

Given: "localhost:8080/my-cool-app"
opened in a Google Chrome

When: I press the Big Blue Button

Then: "Hello World" is displayed
Enter fullscreen mode Exit fullscreen mode

That might look like BDD testing, with all those Given/When/Then, but it's actually Duck Typing, again and again.

Given: a 🦆
When: you kick the 🦆
Then: it quacks
So: 🦆 is alive
Enter fullscreen mode Exit fullscreen mode

This principle also is known as smoke tests

an acceptance test is preliminary testing to reveal simple failures severe enough to, for example, reject a prospective software release. Smoke tests are a subset of test cases that cover the most important functionality of a component or system.

Let's write a bit more tests

What would you say about this one?

Given: a 🥛 glass of water
Then: it's full
When: you take a sip
Then: it's 70% full

When: you take a sip
Then: it's 40% full

When: you take a sip
Then: 🥛 glass is empty
Enter fullscreen mode Exit fullscreen mode

Probably there are some implementation details. Like 30/30/40 per cent of the water sipped every time. That's too fragile

Given: a 🥛 full glass of water
When: you take 3 sips
Then: 🥛 glass is empty
Enter fullscreen mode Exit fullscreen mode

Much more compact, and sound.

6 vs 9 ?

6/9 is about different perspectives. Like the 🥛glass could be 50% full as well as 50% empty.
What if the same actions affect more that one thing?

Given: you are thirsty 
When: you take 3 sips
Then: you are full
Enter fullscreen mode Exit fullscreen mode

Of course! You action affects not only a 🥛glass, but you as well.
Another example? The opposite this time.

Given: 🌏 is spinning
When: you take 3 sips
Then: 🌏 is still spinning
Enter fullscreen mode Exit fullscreen mode

All our tests are the same:

Given

  • you open some Page, or mount some Component
  • you provide some data, or some props

When

  • you click somewhere
  • you change some props or data
  • you perform some action

Then

  • you again check something
  • you expect to be "somewhere"
 Given 👉 When 👉 Then
Enter fullscreen mode Exit fullscreen mode

Liquid error: internal

What it actually IS? Remove all sugar, remove all extra words. What is THE MAIN?

Given -> When -> Then

Precondition -> Action -> Postcondition

Somewhere -> Action -> Somewhere else

Where we are -> What we do -> Where we are


Long story short - this is how State Machines works. All those BDD tests are testing nothing more but transitions from one State to another State, caused by some event. Nothing more.

Or from some data to some data, like in Redux/Flux

State + Action = New State

state

This image is taken from @davidkpiano presentation Infinitely Better UIs with Finite Automata, and there is also a few xstate-related articles you can read at dev.to (and much more in the wild).

Quack!

However, you know, state machines are great... but while we are talking about them, you probably do not use them in your code.
However, I would not be so sure, even if you really don't use them intentionally, explicitly or even implicitly.

Finite State Machines are all about the finite amount of States something could be in, and by some reason. Like could be little black cloud not rainy? Could large rainy cloud not black? What is your expectations from the cloud? Why do you think it is rainy?

Even if David once said - "disabling a button is not app logic" - "Disabled Button" has to have disabled attribute, or at least looks like disabled. Well, quacks 🦆 like a disabled, so everyone will understand your intent. WAI-ARIA included.

👁: An observable system is one whose internal state can be deeply undertood just by observing its outputs.

So, keeping the main principles of Duck Typing - is it possible to infer the Page State from observing the Page?

What if we know how every page state should quack. Could we find the right duck?


🦆 + action = 🦆


However, is it really the case? Let's recall the case with a cup of water.

Given: you are thirsty 
When: you take 3 sips
Then: you are full
Enter fullscreen mode Exit fullscreen mode

You were in one 🦆, took an action, and now you are in another 🦆. In reality you just drunk some water, and your digestion system reported that your are now ok. In fact you are not yet, the water is yet in your digestion system.
👉 The BDD test is actually testing a derived reaction. Which is even not real.

It tests the RESULT, not the implementation details. You heard it multiple times, and let's recall another ancient knowledge to understand the meaning.


Life is a journey, not the destination.
Enter fullscreen mode Exit fullscreen mode

Well in our case it’s the opposite. The destination is the reality, and the journey is an implementation detail.

To finish the moment, to find the journey’s end in every step of the road, to live the greatest number of good hours, is wisdom.


Allegory of the cave, Plato's, 514ad. Which is, long story short, "The Matrix".

Plato begins by having Socrates ask Glaucon to imagine a cave where people have been imprisoned from childhood. These prisoners are chained so that their legs and necks are fixed, forcing them to gaze at the wall in front of them and not look around at the cave, each other, or themselves.
Behind the prisoners is a fire, and between the fire and the prisoners is a raised walkway with a low wall, behind which people walk carrying objects or puppets "of men and other living things".
The people walk behind the wall so their bodies do not cast shadows for the prisoners to see, but the objects they carry do.
The prisoners cannot see any of what is happening behind them, they are only able to see the shadows cast upon the cave wall in front of them. The sounds of the people talking echo off the walls, and the prisoners believe these sounds come from the shadows.
👉 The shadows are reality for the prisoners because they have never seen anything else; they do not realize that what they see are shadows of objects in front of a fire

shadows on the wall

By now you should understand, that your code, your codestyle, patterns and everything else are "the real objects", but "the prisoners", your users in this case, are able to see only shadows.

Real customers are facing the combination of many unrelated to each other processes, TCP/IP and the way their screens displays the data included.

😉 if it walks like an Application, and it swims like an Application, that's an application, not a random collection of bytes.


Reverse Duck

Reverse Duck is what our E2E tests (should) look like
– open some page, and assert some selector. If that selector exists – then we are on the expected page, and the test continues.

  • We are doing some actions, and again checking some selectors - is our page walks like the right page?
  • doing more actions, and again checking selectors - is our page swims like the right page?
  • Oh, it is not? quack!

Every test starts in one 🦆, and ends in another 🦆. Your ability to test your application is limited to your ability to define those distinct states, and here a picture, explaining a lot, again from one of David's presentation. Write fewer tests this time.

Automated tests
👉On the picture: you are in the State A, and you know "how" to go from it to State B or State C - you can create an automated test checking that your application is working "as designed".

All good applications resemble correct business processes state machines behind.

Having your application described as a state machine you CAN try to move from one State to another State and assert the result. Like:

  • have you reached the right State, as it was designed?
  • could go from the Very Beginning to the Very End?
  • could you test transition between any sibling states?
  • could you start your application in any Specific State? If not then why?
  • could you understand that your application is in some Specific State right now?

Works in a quite obvious way - exactly as we need:

Given: 🦆 > ducks like your Home Page
When: You press The Most Important Button
Then: 🦆 > still ducks like your Home Page 😅
Enter fullscreen mode Exit fullscreen mode

Unfortunately, not many application works that way, especially SPAs - usually, they are a bit more complex. Usually, our applications are Big Machines, composed of Smaller machines - 🦆🦆🦆🦆, the Pack of Ducks.

If you just clicked The Most Important Button - it might open a Modal Dialog - like it added something on the page, something parallel to everything else. A new 🦆.

Given: 🦆 > ducks like your Home Page
When: You press The Most Important Button
Then: 🦆 > still ducks like your Home Page, 
         > and The Modal Dialog
         > and network activity indicator
         > and system await user action
         > ....
         > there are just not “blind monks” describing your Elephant
Enter fullscreen mode Exit fullscreen mode

Good examples of "small machines", many of which could be found inside bigger ones are React Hooks - small and simple state machines. And they can form React Component - a Big Machine, the composition of some data machines (useState) plus effect machines (useEffect).

There is no way you can reproduce a really Big Machine using one State, but who said it should be one?

  • when you are opening your application – you are changing it State. From Closed to Open.
  • when you changing а current page – you are changing a nested machine state or sub-state. From Page A to Page B.
  • when you are changing something on a page, filling inputs in the Form you are not changing anything, except machines inside those inputs.
  • but once you have filled everything a Form State could change – Empty->Invalid->Valid.
  • there are many state machines coexisting in your application. Without proper management it is leading, I'm afraid, to Schizophrenia
  • with proper management, they form Akinator, which is nothing more than a pretty big decision tree, which is, well, Recursive Duck Typing. And Triage.

Alt Text

To prevent any misunderstandings let's define terminology:

  • State – is an internal state of an object, not observable from outside. Like React or Redux state. It could be even NOT used for the current page (There is always some useless pieces of data 😿)
  • Duck State – is a part of an underlying state machine, including the “shadow” of explicit machine observable from outside. And let's call it a Phase.

Phase of a State Machines is the current state this machine is in. Like you are reading article. State machine might be in single one state at the one point of time, but remember 🦆🦆🦆 - there could be parallel and nested machines.

It's easy to distinguish State and Phase:

  • State could be a composite object, with dozen variables inside.
  • Phase always just one. The something major about the subject greatly altering subjects behaviour.

think about the blind monk. The monk as to describe what he experienced in one or two words. Which?
If you are not able to do it - add more monks.

Tier

You cannot represent😅 your application with a simple and only one state machine - the real application is like a puzzle, or even like a Quest.

  • you are in point A
  • you have to play a mini-game, like a boss fight, to move to point B
  • you are in point B
  • ...
  • that minigame is an application inside an application - a nested submachine
  • that forms multi-layered, multi-dimensional or multi-tiered architecture

Or a Timing/Flow/Sequence Diagram, which explains what is happening layer by layer.

Timing Diagram

Multitier architecture is quite common and popular. Many patterns could be considered as multi-tiered - from microservices to MVC.
Usually, the separation between tiers is "logical": Presentation layer, Service Layer, Business logic layer, Data access layer. This is how the majority understands it. Not me.
I don't like this sort of separation, as well as I hate "testing Pyramid" and the separation between unit/integration and E2E tests - it's not logical.

Really, "the difference" between unit and E2E tests that the first usually runs on the "server", and the "second" in the Browser. It's a separation by the tool used to run your tests, not the tests. And that's a good example of abstraction leaking.

  • the first tier is a feature definition. It defines how something should work. This is what you want to build, not how.
  • the second tier is a testing layer. A duck machine, where you have to define how every particular state quacks. This is where Design and Observability met each other.
  • the third tier is the implementation of a specific component. Of a Step. How a single Big Machine should work.
  • is the current duck walking as expected, is current duck quacking as expected.
  • If no duck is quacking – it's a fail.
  • If more than one duck is failing – it's a fail.

Surprisingly – the last tier is helping to re-define the first. No Phases should quack the same. If something is different inside – it should be different outside.
Situations when customer trying to click a save button, without any effect cos it's "disabled", but not from UI perspective – much be not possible.

If this is a major behavioural condition – it shall have its own Phase, own State, and shall have its own Duck🦆.

By fact - is very hard to define the proper requirements, it’s really easy to desire something not you really want.
Like “build it with React and Redux” cannot be a business requirement, and usability, conversion rate and a11y cannot be a technical one.


Let’s refer to another ancient knowledge - the Djinn in the bottle.

  • 🧞‍♂️: here I stand, let’s make it fast - 3 wishes
  • 🙀: I want to be riiiich!
  • 🧞‍♀️: as you wish, 😈

A moment later you are rich. Very rich. And old. And everybody hates you. And trying to kill you. Cos you are Narcos.
Not actually what you wanted, but exactly what you’ve asked for.

Just for your information - there is a film about it - Bedazzled.

Bedazzled narcos

As a test, Elliot wishes for a Big Mac and a large Coke. The Devil takes him to McDonald's and places the order. Elliot has to pay for it, because, "there ain't no such thing as a free lunch." After taking Elliot to her office, based at a nightclub in Oakland, the Devil convinces Elliot to sign her contract, and delivers further wishes. Each wish has Elliot living them out with Alison and his co-workers in surrogate roles. However, the Devil always spoils his wishes by adding something he does not expect or want.

And, to be honest, that evil djinn, who is going to use everything you said against you... is you, who is doing exactly what was asked for, but... the devil is in details. And that you is your project manager.

Well, manager, you’ve got what you deserved. Next time be more precise, wish something you really need.

It's always about your Duck definition. How it quack, what you asked Djinn for, what is the story of the blind man.

Conclusion

The Duck Typing is a pattern matching. Is a way to define what something is by observing it.
It is a way to understand​ what something actually is.
It is a requirement to left some breadcrumbs, some details and nuances​ to be able to distinguish​ one duck from another.
It is how to stop being an evil djinn.
About what does really matters, and what is not.
It's about design, UX, monitoring and observability. As I've said in the beginning - you can hammer literally​ everything with it.

The mental models derived from duck typing might help you develop a more thoughtful​ solution, which would do what you really need, as well as define and clarify why you actually need.

Next time you will be asked​ to do something, just quack.

PS: and here are the slides from the talk

Top comments (0)