Upgrade to Pro — share decks privately, control downloads, hide ads and more …

the Subtle Art of Naming

the Subtle Art of Naming

1. Naming is considered as one of the two complex things in programming. Why is it sometimes difficult to name a class, an interface or variable? Is is something that is complex by nature, or do we, as developpers, make it difficult?

2. To try to answer these questions, we'll use a very simple example. Let's say that all day long, I count peas inside buckets.

3. Let's code it! Creation a peas counter class.

4. Method count with a bucket as argument. Initialization of the number of peas.

5. For each pea shell inside the bucket...

6. ...let's open it (via an external dependency)

7. Now that the shell is opened, we can increment the number of peas accordingly.

8. Once the bucket is empty, we can return the peas count.

9. To use this class, just instanciate a new PeasCounter, and call the count method.
This example, and its code is very simple. It fits my needs, is easy to test and to maintain. And it seems I had no difficulty in naming things.
But there is a lot to say about those 15 lines of code. And we could do totally differently.

10. We could have used a rich model for the pea shell.
Anemic model = class with getters/setters, mutated step by step before being validated
RIch model = class which owns its own business logic and rules, can't be in an invalid state, no need to validate it after a process

11. Instead of getting the pea shell to pass it another service...

12. ...we can open directly the pea shell. Also, that's what happen in reality. We don't need a pea shell opener in real life. That does not even exist.

13. Being a little bit more declarative in our code, could help us to name correctly.
Examples of imperative VS declarative in real life.

14. Here we are imperative. We focus on how we will count peas. With a peas counter.

15. We often name our classes with noums. We rarely use verbs or sentences. Really good when the wording exists. Example in real life. But weird to apply it everytime and to have to invent a concept that does not exist.

16. My intention is to count peas inside a bucket. Ideally.

17. Possible code in PHP. Here our class is named according to our intention.

18. Con: __invoke syntax can seem weird
Pro: more declarative

19. We are used to focus on the developer jargon.

20. Let's say we'd like to opensource our library. And allow the others developers to count peas differently. Creation of an interface.

21. The default implementation of our library implements this interface. But what is this suffix about?

22. We love suffixes. It's comforting. But it leads to a lack of reflection. It prevents from thinking about a good naming.

23. Let's remove the suffix, what happens now? We need to focus on our business. Personally, to count peas, I use a pen and a piece of paper. I draw one stick per pea and count the sticks at the end.

24. Let's call our class PenAndPaper. In our context it makes sense. In another context, it would have a different meaning.

25. Others developers maybe want to count peas just in their head.
One remaining thing to do. How should we call the method of the interface?

26. As there is only one method in this interface, I choose to name it again according to my intention. I want to count peas.

27. We often look for a perfect naming. Precise naming for fuzzy or unclear concepts. Hide business vagueness by giving a "good" name. But we shouldn't. Let's just tell the (hard) truth.
No example in my peas story, it's too simple.

28. Akeneo, entity with family to extract from product to be able to use it in another entity. No "name" for that. Not sexy, but when you use Akeneo and you're aware of the family concept, you clearly know what it means.

29. Examples from a meetup live coding session, where there was one product owner and one developer. Not fancy, but precise and reprensents reality.

30. Ok, let's recode our example by using those concepts.

31. We use a piece of paper and a pen to count peas.

32. To count peas inside a bucket.

33. For each pea shell inside the bucket.

34. We can open it, take the peas inside, and draw a stick for each of them.

35. One the bucket is empty, we can count the sticks.

36. To use this class. We take our pen and paper to count peas inside the bucket.
The code is totally different but does exactly the same thing. The major difference it that is uses only real life/business works.
Not to use everywhere, everytime.
Not a recipe to follow as is. Plenty of others things of this kind to help naming.

37. The worst naming in this presentation is its title.
There was nothing subtle or artistic here.
Naming is "marketing".

38. Instead of naming, we should describe.
To describe,we need to understand the business/the problem we try to solve. Which implies to listen, talk and ask questions to the business experts.
cf. ubiquituous language and event storming

Julien Janvier

October 27, 2017
Tweet

More Decks by Julien Janvier

Other Decks in Programming

Transcript

  1. class PeasCounter { public function count(Bucket $bucket) { $peasCount =

    0; foreach ($bucket->getPeaShells() as $peaShell) { $openedPeaShell = $this->peaShellOpener->open($peaShell); foreach ($openedPeaShell->getPeas() as $pea) { $peasCount++; } } return $peasCount; } } 01 02 03 04 05 06 07 08 09 10 11 12 13
  2. class PeasCounter { public function count(Bucket $bucket) { $peasCount =

    0; foreach ($bucket->getPeaShells() as $peaShell) { $openedPeaShell = $this->peaShellOpener->open($peaShell); foreach ($openedPeaShell->getPeas() as $pea) { $peasCount++; } } return $peasCount; } } 01 02 03 04 05 06 07 08 09 10 11 12 13
  3. class PeasCounter { public function count(Bucket $bucket) { $peasCount =

    0; foreach ($bucket->getPeaShells() as $peaShell) { $openedPeaShell = $this->peaShellOpener->open($peaShell); foreach ($openedPeaShell->getPeas() as $pea) { $peasCount++; } } return $peasCount; } } 01 02 03 04 05 06 07 08 09 10 11 12 13
  4. class PeasCounter { public function count(Bucket $bucket) { $peasCount =

    0; foreach ($bucket->getPeaShells() as $peaShell) { $openedPeaShell = $this->peaShellOpener->open($peaShell); foreach ($openedPeaShell->getPeas() as $pea) { $peasCount++; } } return $peasCount; } } 01 02 03 04 05 06 07 08 09 10 11 12 13
  5. class PeasCounter { public function count(Bucket $bucket) { $peasCount =

    0; foreach ($bucket->getPeaShells() as $peaShell) { $openedPeaShell = $this->peaShellOpener->open($peaShell); foreach ($openedPeaShell->getPeas() as $pea) { $peasCount++; } } return $peasCount; } } 01 02 03 04 05 06 07 08 09 10 11 12 13
  6. class PeasCounter { public function count(Bucket $bucket) { $peasCount =

    0; foreach ($bucket->getPeaShells() as $peaShell) { $openedPeaShell = $this->peaShellOpener->open($peaShell); foreach ($openedPeaShell->getPeas() as $pea) { $peasCount++; } } return $peasCount; } } 01 02 03 04 05 06 07 08 09 10 11 12 13
  7. class PeasCounter { public function count(Bucket $bucket) { $peasCount =

    0; foreach ($bucket->getPeaShells() as $peaShell) { $openedPeaShell = $this->peaShellOpener->open($peaShell); foreach ($openedPeaShell->getPeas() as $pea) { $peasCount++; } } return $peasCount; } } 01 02 03 04 05 06 07 08 09 10 11 12 13 $peasCounter = new PeasCounter(...); $peasCount = $peasCounter->count($bucket);
  8. interface CountPeas { //... } class PenAndPaper implements CountPeas {

    //… } class InMemory implements CountPeas { //… }
  9. interface CountPeas { public function countPeas(Bucket $bucket): int; } class

    PenAndPaper implements CountPeas { //… } class InMemory implements CountPeas { //… }
  10. class PenAndPaper implements CountPeas { public function countPeas(Bucket $bucket): int

    { foreach ($bucket->peaShells() as $peaShell) { foreach ($peaShell->open()->peas() as $pea) { $this->drawOneStick(); } } return $this->countSticks(); } } 01 02 03 04 05 06 07 08 09 10 11 12
  11. class PenAndPaper implements CountPeas { public function countPeas(Bucket $bucket): int

    { foreach ($bucket->peaShells() as $peaShell) { foreach ($peaShell->open()->peas() as $pea) { $this->drawOneStick(); } } return $this->countSticks(); } } 01 02 03 04 05 06 07 08 09 10 11 12
  12. class PenAndPaper implements CountPeas { public function countPeas(Bucket $bucket): int

    { foreach ($bucket->peaShells() as $peaShell) { foreach ($peaShell->open()->peas() as $pea) { $this->drawOneStick(); } } return $this->countSticks(); } } 01 02 03 04 05 06 07 08 09 10 11 12
  13. class PenAndPaper implements CountPeas { public function countPeas(Bucket $bucket): int

    { foreach ($bucket->peaShells() as $peaShell) { foreach ($peaShell->open()->peas() as $pea) { $this->drawOneStick(); } } return $this->countSticks(); } } 01 02 03 04 05 06 07 08 09 10 11 12
  14. class PenAndPaper implements CountPeas { public function countPeas(Bucket $bucket): int

    { foreach ($bucket->peaShells() as $peaShell) { foreach ($peaShell->open()->peas() as $pea) { $this->drawOneStick(); } } return $this->countSticks(); } } 01 02 03 04 05 06 07 08 09 10 11 12
  15. class PenAndPaper implements CountPeas { public function countPeas(Bucket $bucket): int

    { foreach ($bucket->peaShells() as $peaShell) { foreach ($peaShell->open()->peas() as $pea) { $this->drawOneStick(); } } return $this->countSticks(); } } 01 02 03 04 05 06 07 08 09 10 11 12 $penAndPaper = new PenAndPaper(); $countPeas = $penAndPaper->countPeas($bucket);