diff --git a/content/books/things-i-learnt/_index.md b/content/books/things-i-learnt/_index.md index b73f417..0568ff0 100644 --- a/content/books/things-i-learnt/_index.md +++ b/content/books/things-i-learnt/_index.md @@ -12,7 +12,11 @@ template = "section-contentless.html" * [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) + * [Testing Every Function Creates Dead Code](tests-dead-code) * [Tests Make Better APIs](tests-apis) * [Make Tests That You Know How To Run on the Command line](tests-in-the-command-line) * [Be Ready To Throw Your Code Away](throw-away) * [Good Languages Come With Tests](languages-tests) + * [Future Thinking Is Future Trashing](future-trashing) + * [Documentation Is a Love Letter To Your Future Self](document-it) + * [The Function Documentation Is Its Contract](document-is-contract) diff --git a/content/books/things-i-learnt/document-is-contract/index.md b/content/books/things-i-learnt/document-is-contract/index.md new file mode 100644 index 0000000..9c02812 --- /dev/null +++ b/content/books/things-i-learnt/document-is-contract/index.md @@ -0,0 +1,37 @@ ++++ +title = "Things I Learnt The Hard Way - The Function Documentation Is Its Contract" +date = 2019-06-21 + +[taxonomies] +tags = ["en-au", "books", "things i learnt", "documentation", "contracts"] ++++ + +When you start the code by [writing the +documentation](/books/things-i-learnt/steps-as-comments), you're actually +making a contract (probably with your future self): I'm saying this function +does _this_ and _this_ is what it does. + + + +Remember that the documentation must be a clear explanation of what your code +_is_ doing; remember that good messages will make [reading the code only by +the function documentation](/books/things-i-learnt/document-id) should be +clear. + +A function called `mult`, documented as "Get the value and multiply by 2" but, +when you look at the code, it does multiply by 2, but sends the result through +the network or even just asks a remote service to multiply the incoming result +by 2, is clearly breaking its contract. It's not just multiplying by 2, it's +doing more than just that, or it's asking someone else to manipulate the +value. + +Now, what happens when this kind of thing happens? + +The easy solution is to change the documentation. But do you know if people +who called the function expecting it to be "multiply value by 2" will be happy +for it to call an external service? There is a clear breach of "contract" -- +whatever you initially said your function would do -- so the correct solution +would be to add a new function with a proper contract -- and probably a better +name. + +{{ chapters(prev_chapter_link="/books/things-i-learnt/document-it", prev_chapter_title="Documentation Is a Love Letter To Your Future Self") }} diff --git a/content/books/things-i-learnt/document-it/index.md b/content/books/things-i-learnt/document-it/index.md new file mode 100644 index 0000000..fe34aa0 --- /dev/null +++ b/content/books/things-i-learnt/document-it/index.md @@ -0,0 +1,28 @@ ++++ +title = "Things I Learnt The Hard Way - Documentation Is a Love Letter To Your Future Self" +date = 2019-06-21 + +[taxonomies] +tags = ["en-au", "books", "things i learnt", "documentation"] ++++ + +We all know writing the damn docs for functions and classes and modules is a +pain in the backside. But realizing what you were thinking when you wrote the +function will save your butt in the future. + + + +When I say that it will save your butt, I don't mean the documentation will +tell you something like "Here are the lotto numbers in 2027"[^1] or "If John +complains about your future code review, here is some shit he did in the +past". + +I mean, it will explain how the _flow_ of your code is expected to do. Imaging +this: pick your code and replace every function call to its documentation. Can +you understand what it is expected by reading that? If you can, +congratulations, you won't have a problem in the future; if you can't... well, +I have some bad news for you... + +[^1]: Please, don't make me revise this in 2027... :( + +{{ chapters(prev_chapter_link="/books/things-i-learnt/future-trashing", prev_chapter_title="Future Thinking is Future Trashing", next_chapter_link="/books/things-i-learnt/document-is-contract", next_chapter_title="The Function Documentation Is Its Contract") }} diff --git a/content/books/things-i-learnt/future-trashing/index.md b/content/books/things-i-learnt/future-trashing/index.md new file mode 100644 index 0000000..1b7874b --- /dev/null +++ b/content/books/things-i-learnt/future-trashing/index.md @@ -0,0 +1,26 @@ ++++ +title = "Things I Learnt The Hard Way - Future Thinking is Future Trashing" +date = 2019-06-21 + +[taxonomies] +tags = ["en-au", "books", "things i learnt", "design", "solution"] ++++ + +When developers try to solve a problem, they sometimes try to find a way that +will solve all the problems, including the ones that may appear in the future. + + + +Trying to solve the problems that will appear in the future comes with a hefty +tax: future problems future will never come -- and, believe me, they _never_ +come -- and you'll end up either having to maintain a huge behemoth of code +that will never be fully used or you'll end up rewriting the whole thing +'cause there is a shitton of unused stuff. + +Solve the problem you have right now. Then solve the next one. And the next +one. At one point, you'll realize there is a pattern emerging from those +solutions and _then_ you'll find your "solve everything". This pattern is the +_abstraction_ you're looking for and _then_ you'll be able to solve it in a +simple way. + +{{ chapters(prev_chapter_link="/books/things-i-learnt/languages-tests", prev_chapter_title="Good Languages Come With Tests", next_chapter_link="/books/things-i-learnt/document-id", next_chapter_title="Documentation Is a Love Letter To Your Future Self") }} diff --git a/content/books/things-i-learnt/integration-tests/index.md b/content/books/things-i-learnt/integration-tests/index.md index 77c5b50..b62f80c 100644 --- a/content/books/things-i-learnt/integration-tests/index.md +++ b/content/books/things-i-learnt/integration-tests/index.md @@ -66,4 +66,4 @@ parts. [^1]: There is no "unit" in "unit tests". "Unit test" means the test _is_ a unit, indivisible and dependent only on itself. -{{ chapters(prev_chapter_link="/books/things-i-learnt/gherkin", prev_chapter_title="Gherkin Is Your Friend to Understand Expectations", next_chapter_title="Tests Make Better APIs", next_chapter_link="/books/things-i-learnt/tests-apis") }} +{{ chapters(prev_chapter_link="/books/things-i-learnt/gherkin", prev_chapter_title="Gherkin Is Your Friend to Understand Expectations", next_chapter_title="Testing Every Function Creates Dead Code", next_chapter_link="/books/things-i-learnt/tests-dead-code") }} diff --git a/content/books/things-i-learnt/languages-tests/index.md b/content/books/things-i-learnt/languages-tests/index.md index e75dc71..2f0d7e1 100644 --- a/content/books/things-i-learnt/languages-tests/index.md +++ b/content/books/things-i-learnt/languages-tests/index.md @@ -22,4 +22,4 @@ testing framework in their standard library may have options with better support and easier access but, again, when they are there from the start, the start is better and the final result is better. -{{ chapters(prev_chapter_link="/books/things-i-learnt/throw-away", prev_chapter_title="Be Ready To Throw Your Code Away") }} +{{ chapters(prev_chapter_link="/books/things-i-learnt/throw-away", prev_chapter_title="Be Ready To Throw Your Code Away", next_chapter_link="/books/things-i-learnt/future-trashing", next_chapter_title="Future Thinking is Future Trashing") }} diff --git a/content/books/things-i-learnt/tests-dead-code/index.md b/content/books/things-i-learnt/tests-dead-code/index.md new file mode 100644 index 0000000..5239952 --- /dev/null +++ b/content/books/things-i-learnt/tests-dead-code/index.md @@ -0,0 +1,59 @@ ++++ +title = "Things I Learnt The Hard Way - Testing Every Function Creates Dead Code" +date = 2019-06-21 + +[taxonomies] +tags = ["en-au", "books", "things i learnt", "unit tests", "dead code"] ++++ + +If you write a test for every single function on your system, and your system +keeps changing, how will you know when a function is not necessary anymore? + + + +Writing a test for every single function on your system may come from the +"100% Coverage Syndrome", which afflicts some managers, thinking that the only +way to be completely sure your system is "bug free" is to write tests for +every single piece of code, till you reach the magical "100% coverage" in all +the tests. + +I do believe you can reach 100% coverage, as long as you're willing to +_delete_ your code. + +Cue the universal grasps here. + +But how do you know which pieces of code can be deleted? + +When I mentioned [integration +tests](/books/things-i-learnt/integration-tests), I mentioned how much more +sense it made to me reading them instead of the "unit" tests, because they +were describing exactly how the system would operate in normal conditions. If +you write tests the go through the system, doing normal operations, and you +can get tests for all the normal cases -- and some "abnormal", like when +things go wrong -- then you know that, if you run those tests and they mark +some lines as "not tested", it's because you don't need them. + +"But Julio, you're forgetting the error control!" I do agree, specially when +you're talking with project owners or some other expert, that people will +forget to tell you what to do in case of things going wrong -- say, someone +entering a name in the age field -- but _you_ can see those and _you_ know +that you need error control so _you_ can add the error control and describe +the situation where that error control would trigger. + +If, on the other hand, you write a test for every function, when you do a +short/simple check, you'll find that the function is still being used in the +system -- by the tests, not actually, "value to the user" code. Sure, you can +use your IDE to go back and forth between code and test and see if it points a +use beyond the test, but it won't do it for yourself. + +There is one other weird thing about trying to write integration tests for +error controls: Sometimes, you can't reach the control. It's true! I did wrote +control checks for every function once but, when running in the integration +tests, there was no way to produce an input at the input layer of the system +that would reach the error control in that function -- mostly 'cause the +other functions, which would run before the one I was trying to test, would +catch the error before it. If that's a design problem or not -- it probably +was -- it's a different discussion, but the fact is that that function didn't +need error control. + +{{ chapters(prev_chapter_link="/books/things-i-learnt/integration-tests", prev_chapter_title="Unit Tests Are Good, Integration Tests Are Gooder", next_chapter_title="Tests Make Better APIs", next_chapter_link="/books/things-i-learnt/tests-apis") }} diff --git a/content/thoughts/things-i-learnt-the-hard-way.md b/content/thoughts/things-i-learnt-the-hard-way.md index 9e1ee19..fbc8c2a 100644 --- a/content/thoughts/things-i-learnt-the-hard-way.md +++ b/content/thoughts/things-i-learnt-the-hard-way.md @@ -171,7 +171,7 @@ you can finally kill the original function. function as deprecated and _add a sleep at the start of the function_, in a way that people using the old function are forced to update.) -### Good languages come with integration documentation +### Good languages come with integrated documentation If the language comes with its own way of documenting functions/classes/modules/whatever and it comes even with the simplest doc