Julio Biason
6 years ago
10 changed files with 335 additions and 9 deletions
@ -1,8 +1,13 @@
|
||||
+++ |
||||
title = "My Books" |
||||
template = "section-contentless.html" |
||||
transparent = true |
||||
+++ |
||||
|
||||
## Portuguese/Português |
||||
|
||||
* [Uma Lição de Vim](uma-licao-de-vim) |
||||
|
||||
## English/Inglês |
||||
|
||||
* [Things I Learnt The Hard Way](things-i-learnt) |
||||
|
@ -0,0 +1,14 @@
|
||||
+++ |
||||
transparent = true |
||||
title = "Things I Learnt The Hard Way (In 30 Years of Software Development)" |
||||
template = "section-contentless.html" |
||||
+++ |
||||
|
||||
* [Intro](intro) |
||||
* [Disclaimer](disclaimer) |
||||
|
||||
* Programming: |
||||
* [Spec First, Then Code](spec-first) |
||||
* [Write Steps as Comments](steps-as-comments) |
||||
* [Gherkin Is Your Friend to Understand Expectations](gherkin) |
||||
* [Unit Tests Are Good, Integration Tests Are Gooder](integration-tests) |
@ -0,0 +1,42 @@
|
||||
+++ |
||||
title = "Things I Learnt The Hard Way - Disclaimer" |
||||
date = 2019-06-19 |
||||
|
||||
[taxonomies] |
||||
tags = ["en-au", "books", "things i learnt", "disclaimer"] |
||||
+++ |
||||
|
||||
There is one magical thing you need to know when reading this book: It's all |
||||
personal opinion |
||||
|
||||
<!-- more --> |
||||
|
||||
A lot of stuff I'm going to discuss throughout this book will come directly |
||||
from my personal experience in several projects -- system applications, web |
||||
backend, embedded, mobile, stream processing -- in several different languages |
||||
-- C, C++, Python, Java. And, because it comes from personal experience, |
||||
everything reflects my own personal opinion on several subjects. |
||||
|
||||
Obviously, you don't need to agree with every single point. |
||||
|
||||
Also, sometimes I may mention some examples that people who know me -- either |
||||
worked with me, heard me complain about some project, inherit one of my |
||||
projects, _I_ inherit one of the _their_ projects -- may recognized and think |
||||
I'm attacking the author. |
||||
|
||||
I am not. |
||||
|
||||
We do mistakes. Sometimes we don't know the topic with are attacking, |
||||
sometimes we don't have full specs, sometimes we don't have the time to write |
||||
things properly in a crunchtime. And that's why some things don't look as |
||||
pretty as they should. Heck, if you think I'm attacking the original author of |
||||
some example, look back the stuff I wrote and you'll see things a lot worse. |
||||
|
||||
But I need the example. I want to show people how things can be better. I want |
||||
to show people how my opinion built over some subject. And, again, I'm in no |
||||
way attacking the original author of the code. I may even call the code |
||||
"stupid", but I'm not calling the author _stupid_. |
||||
|
||||
With that in mind... |
||||
|
||||
{{ chapters(prev_chapter_link="/books/things-i-learnt/intro", prev_chapter_title = "Intro", next_chapter_link="/books/things-i-learnt/spec-first", next_chapter_title="Spec First, The Code") }} |
@ -0,0 +1,54 @@
|
||||
+++ |
||||
title = "Things I Learnt The Hard Way - Gherkin Is Your Friend to Understand Expectations" |
||||
date = 2019-06-19 |
||||
|
||||
[taxonomies] |
||||
tags = ["en-au", "book", "things i learnt", "gherkin", "expectations"] |
||||
+++ |
||||
|
||||
Gherkin is file format for writing behaviour tests. But it can also give you |
||||
some insights on what you should do. |
||||
|
||||
<!-- more --> |
||||
|
||||
Alright, let's talk a bit about Gherkin: |
||||
|
||||
[Gherkin](https://en.wikipedia.org/wiki/Cucumber_(software)#Gherkin_language) |
||||
is a file format created for [Cucumber](https://en.wikipedia.org/wiki/Cucumber_(software)), |
||||
which describes scenarios, what's in them, what actions the user/system will |
||||
do and what's expected after those actions, in a very high level, so people |
||||
without programming experience can describe what's expected from the system. |
||||
|
||||
Although Gherkin was born with Cucumber, it is now supported by a bunch of |
||||
programming languages, through external libraries. |
||||
|
||||
A typical Gherkin file may look something like this: |
||||
|
||||
* **Given that** _initial system environment_ |
||||
* **When** _action performed by the user or some external system_ |
||||
* **Then** _expected system environment_ |
||||
|
||||
Or, in a more concrete example: |
||||
|
||||
* **Given that** The system is retrieving all tweets favourited by the user |
||||
* **When** It finds a tweet with an attachment |
||||
* **Then** The attachment should be saved along the tweet text |
||||
|
||||
Pretty simple, right? |
||||
|
||||
Now, why I'm mentioning this? |
||||
|
||||
Sometimes, specs are not the most clear source of information about what it is |
||||
expected from the system. If you're confused about what you should write, |
||||
asking the person responsible for the request to write something like Gherkin |
||||
may give you some better insights about it. |
||||
|
||||
Obviously, it won't be complete. People tend to forget the error situations -- |
||||
people entering just numbers on names, letter in age fields, tweets with no |
||||
text and just attachments -- but at least with a Gherkin description of the |
||||
system, you can get a better picture of the whole. |
||||
|
||||
Also, you may not like to write specs. That's alright, you can replace them |
||||
with Gherkin anyway. |
||||
|
||||
{{ chapters(prev_chapter_link="/books/things-i-learnt/steps-as-comments", prev_chapter_title="Write Steps as Comments", next_chapter_link="/books/things-i-learnt/integration-tests", next_chapter_title="Unit Tests Are Good, Integration Tests Are Gooder") }} |
@ -0,0 +1,69 @@
|
||||
+++ |
||||
title = "Things I Learnt The Hard Way - Unit Tests Are Good, Integration Tests Are Gooder" |
||||
date = 2019-06-19 |
||||
|
||||
[taxonomies] |
||||
tags = ["en-au", "book", "things i learnt", "unit tests", "integration tests"] |
||||
+++ |
||||
|
||||
The view of the whole is greater than the sum of its parts. And that includes |
||||
tests for the whole compared to tests of single things. |
||||
|
||||
<!-- more --> |
||||
|
||||
First, I just don't want to into a discussion about what's the "unit in a unit |
||||
test"[^1], so let's take the point that a unit test is a test that tests a |
||||
class/function, not the whole system, which would require data flowing through |
||||
several classes/functions. |
||||
|
||||
There are several libraries/frameworks that actually split this in a way that |
||||
you can't test the whole. |
||||
[Spring](https://spring.io/)+[Mockito](https://site.mockito.org/) is one of |
||||
those combinations -- and one that I worked with. Due the bean container of |
||||
Java, the extensive use of Beans by Spring and the way Mockito interacts with |
||||
the container, it's pretty easy to write tests that involve only one class: |
||||
You can ask Mockito to mock every dependency injection in one class and mock |
||||
every injected class, simply using annotations. |
||||
|
||||
And this is cool and all. But the fact that we are making sure each class does |
||||
what it does, it doesn't give a proper view of the whole; you can't see if |
||||
that collection of perfectly tested classes actually solve the problem the |
||||
system is responsible for solving. |
||||
|
||||
Once, in C++, I wrote an alarm system |
||||
[daemon](https://en.wikipedia.org/wiki/Daemon_(computing)) for switches. There |
||||
were three different levels of things the alarm system should do: It could |
||||
only log the message of the incoming error, it could log the error and send a |
||||
SNMP message, or it could log the error, send a SNMP message and turn a LED in |
||||
the front panel on. Because each piece had a well defined functionality, we |
||||
broke the system in three different parts: One for the log, one for the SNMP |
||||
and one for the LED. All tested, all pretty. But I still had a nagging |
||||
feeling that something was missing. That's when I wrote a test that would |
||||
bring the daemon up, send some alarms and see the results. |
||||
|
||||
And, although each module was well tested, we still got one things we were |
||||
doing it wrong. If we never wrote an integration test, we would never catch |
||||
that. |
||||
|
||||
Not only that, but because we wrote a test that interacted with the daemon, we |
||||
could get a better picture of its functionality and the test actually _made |
||||
sense_ -- as in, if you read the unit tests, they seemed disconnected from |
||||
what the daemon was expected to do, but the integration tests actually read |
||||
like "Here, let me show that we actually did what you asked". And yes, this |
||||
was akin to [Gherkin](/books/things-i-learnt/gherkin) tests, although I didn't |
||||
know Gherkin at the time. |
||||
|
||||
Personally, I think over time integration tests are more important that unit |
||||
tests. The reason is that I still have the feeling that unit tests check if |
||||
the classes/functions have _adherence_ to the underlying _design_ -- Does your |
||||
view can actually work without the controller? Is the controller using |
||||
something from the model or using things that should be in the view? -- but |
||||
adherence to the design gets better over time -- developers start using the |
||||
layout from previous examples, so they capture the design by osmosis, while |
||||
the big picture starts to get more and more complex, with lots of moving |
||||
parts. |
||||
|
||||
{{ chapters(prev_chapter_link="/books/things-i-learnt/gherkin", prev_chapter_title="Gherkin Is Your Friend to Understand Expectations") }} |
||||
|
||||
[^1]: There is no "unit" in "unit tests". "Unit test" means the test _is_ a |
||||
unit, indivisible and dependent only on itself. |
@ -0,0 +1,52 @@
|
||||
+++ |
||||
title = "Things I Learnt The Hard Way - Intro" |
||||
date = 2019-06-18 |
||||
|
||||
[taxonomies] |
||||
tags = ["en-au", "books", "things i learnt", "intro"] |
||||
+++ |
||||
|
||||
"Things I Learnt The Hard Way (In 30 Years of Software Development)" started |
||||
as a simple sequence of toots (the same as "tweets", but outside Twitter) when |
||||
I was thinking about a new presentation I could do. |
||||
|
||||
But why "a new presentation"? |
||||
|
||||
<!-- more --> |
||||
|
||||
I go around my state with a group called "Tchelinux": We usually go to |
||||
universities and talk to people starting uni, explaining things about |
||||
free/libre software and sometimes telling people about things they wouldn't |
||||
normally see in the uni curriculum. |
||||
|
||||
One thing that annoys me is that there are very few presentations about "when |
||||
things go wrong". All the presentations are either prototypes or tell the good |
||||
stuff, and hide all the wrong things that could happen[^1]. Obviously, after |
||||
working 30 years in the field of software development, I saw my fair share of |
||||
things going wrong -- sometimes in unimaginable piles of crap -- and I thought |
||||
"maybe that's something people would like to hear". |
||||
|
||||
And that's when the toot sequence started. Just before I noticed, I spent the |
||||
whole day just posting this kind of stuff (fortunately, my pile of "incoming" |
||||
was a bit empty at the time) and it had 30 points, plus addendums and a few |
||||
explanation points. That's when I decided to group all them in a single post. |
||||
|
||||
All I thought when I grouped everything in a post was "this will make things |
||||
easier for the people following the thread on Mastodon". But then the post |
||||
appeared on Reddit. And Twitter. And HackerNews. And YCombinator. And none of |
||||
those where mine. |
||||
|
||||
But here is the thing: Each point was limited by the toot size, which is 500 |
||||
characters. Sometimes that's not enough to expand the point, explain it |
||||
properly and add some examples. |
||||
|
||||
And that's how the idea to write this "book" came to life. |
||||
|
||||
One thing you must keep in mind here: *These are my options*. I understand |
||||
that not everything is so black and white as put here, and some people's |
||||
experiences may not match things here. Also, you get a bit cynical about |
||||
technology after 30 years. So... thread carefully, 'cause here be dragons. |
||||
|
||||
[^1]: Yup, I'm guilty of that too. |
||||
|
||||
{{ chapters(next_chapter_link="/books/things-i-learnt/disclaimer", next_chapter_title="Disclaimer") }} |
@ -0,0 +1,40 @@
|
||||
+++ |
||||
title = "Things I Learnt The Hard Way - Spec First, Then Code" |
||||
date = 2019-06-18 |
||||
|
||||
[taxonomies] |
||||
tags = ["en-au", "books", "things i learnt", "specs", "code"] |
||||
+++ |
||||
|
||||
"Without requirements or design, programming is the art of adding bugs to an |
||||
empty text file." -- Louis Srygley |
||||
|
||||
<!-- more --> |
||||
|
||||
If you don't know what you're trying to solve, you don't know what to code. |
||||
|
||||
A lot of times we have this feeling of "let me jump straight to the code". But |
||||
without understanding what problem you're trying to solve, you'd end up |
||||
writing a bunch of things that doesn't solve anything -- or, at least, |
||||
anything that _should_ be solved. |
||||
|
||||
So here is the point: Try to get a small spec on whatever you want to solve. |
||||
But be aware that even that spec may have to be thrown out, as the |
||||
understanding of the problem tend to grow as long as the project continue. |
||||
|
||||
Yes, it's paradoxical: You need a spec to know what to code to avoid coding |
||||
the wrong solution, but the spec may be wrong, so you _end up_ solving the |
||||
wrong solution anyway. So what's the point? The point is, the spec reflects |
||||
the understanding of a problem _at a certain point_: All you (and your team) |
||||
know is _there_. |
||||
|
||||
The times I stood longer looking at my own code wondering what to do next were |
||||
when we didn't have the next step defined: It was missing some point of the |
||||
solution or we didn't have the communication structures defined or something |
||||
of sorts. Usually, when that happened, I stumbled upon Twitter or Mastodon |
||||
instead of trying to solve the problem. So when you see yourself doing this |
||||
kind of stuff -- "I don't know what to do next, and I'm not sure if I'm done |
||||
with the current problem" -- then maybe it's time to stop and talk to other |
||||
people in the project to figure that out. |
||||
|
||||
{{ chapters(prev_chapter_link="/books/things-i-learnt/disclaimer", prev_chapter_title="Disclaimer", next_chapter_link="/books/things-i-learnt/steps-as-comments", next_chapter_title="Write Steps as Comments") }} |
@ -0,0 +1,58 @@
|
||||
+++ |
||||
title = "Things I Learnt The Hard Way - Write Steps as Comments" |
||||
date = 2019-06-18 |
||||
|
||||
[taxonomies] |
||||
tags = ["en-au", "books", "things i learnt", "steps", "comments", "code"] |
||||
+++ |
||||
|
||||
Don't know how to solve your problem? Write the steps as comments in your |
||||
code. |
||||
|
||||
<!-- more --> |
||||
|
||||
There you are, looking at the blank file wondering how you're going to solve |
||||
that problem. Here is a tip: |
||||
|
||||
Take the spec you (or someone else) wrote. Break each point into a series of |
||||
steps to reach the expected content. You can even write on your natural |
||||
language, if you don't speak English. |
||||
|
||||
Then fill the spaces between the comments with code. |
||||
|
||||
For example, if you have a spec of "connect to server X and retrieve |
||||
everything there. You should save the content in the database. Remember that |
||||
server X has an API that you can pass an ID (the last ID seen) and you can use |
||||
it to not retrieve the same content again." Pretty simple, right? |
||||
|
||||
Now, writing this in comments, pointing the steps you need to make: |
||||
|
||||
``` |
||||
// connect to server X |
||||
// retrieve posts |
||||
// send posts to the database |
||||
``` |
||||
|
||||
Ah, you forgot the part about the ID. No problem, you just have to add it in |
||||
the proper places -- for example, it doesn't make sense to connect to the |
||||
server before you have the last seen ID: |
||||
|
||||
``` |
||||
// open configuration file |
||||
// get value of the last seen ID; if it doesn't exist, it's empty. |
||||
// connect to server X |
||||
// retrieve posts starting at the last seen ID |
||||
// send posts to the database |
||||
// save the last seen ID in the configuration file |
||||
``` |
||||
|
||||
Now it is "easy"[^1]: You just add the code after each comment. |
||||
|
||||
A better option is to change the comments into functions and, instead of |
||||
writing the code between the comments, you write the functionality in the |
||||
function themselves and keep a clean view of what your application does in the |
||||
main code. |
||||
|
||||
[^1]: Yes, that was sarcastic. |
||||
|
||||
{{ chapters(prev_chapter_link="/books/things-i-learnt/spec-first", prev_chapter_title="Specs First, Then Code", next_chapter_link="/books/things-i-learnt/gherkin", next_chapter_title="Gherkin Is Your Friend to Understand Expectations") }} |
Loading…
Reference in new issue