{"id":7616,"date":"2018-06-13T15:30:17","date_gmt":"2018-06-13T18:30:17","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=7616"},"modified":"2018-06-13T15:54:58","modified_gmt":"2018-06-13T18:54:58","slug":"the-anatomy-of-code-documentation","status":"publish","type":"post","link":"http:\/\/blog.plataformatec.com.br\/2018\/06\/the-anatomy-of-code-documentation\/","title":{"rendered":"The anatomy of code documentation"},"content":{"rendered":"
Writing code is the activity that turns our ideas into great products and tools.<\/span><\/p>\n Fingers over a keyboard and a text editor. This is how most people see our daily routine. Suddenly, no words emerge over the screen. The eyes gaze at the monitor and everything looks static: we\u2019re reading code.<\/span><\/p>\n (And we read lots<\/strong> of code).<\/span><\/p>\n The reading process demands a huge cognitive effort from us. Once we read a piece of code for the very first time, we\u2019re immersed in a long learning curve and a continuous cycle of questionings, doubts, and insights. Reading code requires us to dig through the mental model of a domain problem and to continuously analyze the input, processing and output flows of some business requirement.<\/span><\/p>\n Sure, good developers have been exposed to good writing practices, and there is nothing like reading a clean, robust and elegant piece of code. Our methods are small and expressive and intend to reveal interfaces. <\/span><\/p>\n Nevert<\/span>heless, we still must read them line by line, and gradually lay down our understanding about it.<\/p>\n (“Only well written and tested code is not enough to connect the dots\u2026”, some wise person may have already said this before.)<\/em><\/p>\n A large number of whiteboard discussions may have been lost, merely because there was no room for those. Commit messages were laconic and our memorization skills were overestimated. Yesterday\u2019s obvious is now a mine of untangled knowledge.<\/p>\n Furthermore, the practice of comment about code is often discouraged. After all, we prioritize working software over overwhelming documentation. We\u2019re agile developers and we cannot afford the maintenance burden. Code comments are mostly faced as the inability to express oneself in a clever way, and the activity of documenting code is usually associated with redundant, undesired and ungrateful comments.<\/p>\n But here it goes: the key point on documenting is maintaining information that resists as time goes by.<\/strong><\/p>\n Suppose our sample application deals with a concept of contracted plans on its domain, where each plan offers a storage limit in a remote storage service. One of the requirements is to present a summary of a contracted plan\u2019s consumption, as shown in the example below:<\/p>\n Our source data is a domain object The consumption summary must present these two pieces of information, in addition to the percentage of how much has been consumed until now:<\/p>\n The percentage of how much has been consumed can be calculated simply applying the following statement:<\/p>\n Knowing that such summary statement must be rendered in an HTML page, we\u2019re gonna create a The There are, of course, some ways to find out how the returned text will be printed out:<\/p>\n All alternatives, however, requires the reader to make an additional effort. All of them encourage the reader to leave out its current context, which is reading the application code in the text editor. In that case, we can leverage code documentation as a technique to facilitate the expected understanding.<\/p>\n First of all, the The first goal was accomplished. We have diminished the learning barrier to the method logic, without breaking the flow of reading code. Moreover, by being exposed to real data examples, we had a better understanding of some concepts present in the application domain, at a very low cost.<\/p>\n The next snippet that we may add is a title that describes briefly what is the method in that case. Let’s think for a moment. How would we describe the A possible dialog could be:<\/p>\n \n Well, this method returns a summary of how much a user has consumed in a certain plan.\n<\/p><\/blockquote>\n Bringing the same information to the code, we have this:<\/p>\n By describing the title of a method, we have the chance to reflect and identify whatever noise is between a concept and its implementation. The mere act of writing documentation helps us to give remarkable names to our objects, methods, and variables. If the previously written code has been derived only from what was talked or listened, now we have a third communication channel.<\/p>\n At this moment, our documentation has two pieces of information with different semantics: a title and an example. We can apply to it a bit of hierarchy, (i.e., setting apart free text and code), we can make this structure a bit more evident:<\/p>\n We can also add up a piece of information that points out that the method has a public visibility and, therefore, can be used by outside application code (i.e., the view layer). In our example, we use the The third point that we can cover is about the type\/shape of data that will be returned from the method. Since we know that the return type will be a String, we can add such information with In order to complete the input-processing-output triad, we also describe the expected input data, along with a brief description of each snippet. Our case is simple, as we just want an instance of We have now a basic backbone of code documentation. We know how to answer the “what’s” and “how’s” intrinsic to the We know context is everything. Unfortunately, there is information that might be lost in time, especially after some cycles of team rotation and turnovers. The context can help us out to surface the aspects of “when to use a method” and “the reason\/intentions behind using this”.<\/p>\n In our example, we can describe something that conveys the following message:<\/p>\n \n The Assuming that such information is relevant, we can include it on the code documentation:<\/p>\n We can consider the job done now.<\/p>\n It is then a good time to notice that the format used in our example was not conceived by my free will. Attentive readers may have perceived that we used the TomDoc<\/a> format, created by Tom Preston-Werner. The TomDoc spec aims for simplicity and clarity of communication and it is hugely used in GitHub projects (and here at Plataformatec as well).<\/p>\n Conclusion<\/strong><\/p>\n An important goal of writing code documentation is to provide an initial context to facilitate and foster the understanding of a piece of code. It’s an empathetic tool that provides a rough, overall idea of the code, even before “one starts reading the code”. By using it wisely, the reader can get a glimpse of what will be executed by the language interpreter\/compiler, with little effort.<\/p>\n Code documentation is, of course, only one of many other artifacts that are at our disposal, in order to keep the knowledge base of a software system alive. I consider that as important as having well-described commit messages, README files, project wikis, architectural decision records, BDD specification. etc. They all work together focused on the same essential goal: empower developers to write better software.<\/p>\n","protected":false},"excerpt":{"rendered":" Writing code is the activity that turns our ideas into great products and tools. Fingers over a keyboard and a text editor. This is how most people see our daily routine. Suddenly, no words emerge over the screen. The eyes gaze at the monitor and everything looks static: we\u2019re reading code. (And we read lots … \u00bb<\/a><\/p>\n","protected":false},"author":20,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[224,291],"aioseo_notices":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/7616"}],"collection":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/users\/20"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/comments?post=7616"}],"version-history":[{"count":13,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/7616\/revisions"}],"predecessor-version":[{"id":7624,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/7616\/revisions\/7624"}],"wp:attachment":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/media?parent=7616"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/categories?post=7616"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/tags?post=7616"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}A practical example<\/h2>\n
'2.44% (500 MB of 20 GB)' # User has consumed 2.44% from a 20 gigabytes\n # storage limit (i.e., 500 megabytes).\n<\/code><\/pre>\n
ContractedPlan<\/code>, which exposes two main pieces of information: the storage limit value (in the
plan_storage_limit column<\/code>) and the total consumed (in the
total_consumed<\/code> column) until the present moment, both persisted in numeric bytes in the database.<\/p>\n
\\> contracted_plan = ContractedPlan.last\n\n\\> contracted_plan.plan_storage_limit # Client has contracted a 20 GB plan.\n# => 20_000_000_000\n\n\\> contracted_plan.total_consumed # User has consumed 500 megabytes until now.\n# => 500_000_000\n<\/code><\/pre>\n
\\> total_consumed_in_percent =\n (total_consumed * 100.to_f \/ total_contracted).round(2)\n# => 2.44\n<\/code><\/pre>\n
ContractedPlansHelper#plan_consumption_summary<\/code> helper with the following implementation:<\/p>\n
module ContractedPlansHelper\n def plan_consumption_summary(contracted_plan)\n total_contracted = contracted_plan.plan_storage_limit\n total_consumed = contracted_plan.total_consumed\n total_consumed_in_percent =\n (total_consumed * 100.to_f \/ total_contracted).round(2)\n\n format(\n '%s%% (%s de %s)',\n total_consumed_in_percent,\n number_to_human_size(total_consumed, precision: 4),\n number_to_human_size(total_contracted)\n )\n end\nend\n<\/code><\/pre>\n
#plan_consumption_summary<\/code> method has a structure divided into two main parts: data preparation and data presentation (including the step for formatting the three expected pieces of information).
\nEven though it looks like a simple and straight logic, there are some points not so obvious for newcomers to that code. The data presentation part depends on a moderately complex structure, in a way that is not an easy piece to digest regarding the “shape” of the returned String (i.e., the syntax of the format method, besides the two calls for the #number_to_human_size method<\/code>).<\/p>\n
\n
plan_consumption_summary<\/code> method can provide a simple usage example, with real data expected from business customers:<\/p>\n
# plan_consumption_summary(contracted_plan)\n# # => '2.44% (500 MB of 20 GB)'\ndef plan_consumption_summary(contracted_plan)\n total_contracted = contracted_plan.plan_storage_limit\n total_consumed = contracted_plan.total_consumed\n # ...\n<\/code><\/pre>\n
plan_consumption_summary<\/code> to someone who has just joined the project?<\/p>\n
# A summary of how much some user has consumed in a certain plan.\n#\n# plan_consumption_summary(contracted_plan)\n# # => '2.44% (500 MB of 20 GB)'\ndef plan_consumption_summary(contracted_plan)\n total_contracted = contracted_plan.plan_storage_limit\n total_consumed = contracted_plan.total_consumed\n # ...\n<\/code><\/pre>\n
# A summary of how much some user has consumed in a certain plan.\n#\n# Examples\n# plan_consumption_summary(contracted_plan)\n# # => '2.44% (500 MB of 20 GB)'\ndef plan_consumption_summary(contracted_plan)\n total_contracted = contracted_plan.plan_storage_limit\n total_consumed = contracted_plan.total_consumed\n # ...\n<\/code><\/pre>\n
Public:<\/code> keyword:<\/p>\n
# Public: A summary of how much some user has consumed in a certain plan.\n#\n# Examples\n# plan_consumption_summary(contracted_plan)\n# # => '2.44% (500 MB of 20 GB)'\ndef plan_consumption_summary(contracted_plan)\n total_contracted = contracted_plan.plan_storage_limit\n total_consumed = contracted_plan.total_consumed\n # ...\n<\/code><\/pre>\n
Returns a String:<\/code><\/p>\n
# Public: A summary of how much some user has consumed in a certain plan.\n#\n# Examples\n# plan_consumption_summary(contracted_plan)\n# # => '2.44% (500 MB of 20 GB)'\n#\n# Returns a String.\ndef plan_consumption_summary(contracted_plan)\n total_contracted = contracted_plan.plan_storage_limit\n total_consumed = contracted_plan.total_consumed\n # ...\n<\/code><\/pre>\n
ContractedPlan<\/code> model, and there is no other primitive values involved.<\/p>\n
# Public: A summary of how much some user has consumed in a certain plan.\n#\n# contracted_plan - A ContractedPlan instance.\n#\n# Examples\n# plan_consumption_summary(contracted_plan)\n# # => '2.44% (500 MB of 20 GB)'\n#\n# Returns a String.\ndef plan_consumption_summary(contracted_plan)\n total_contracted = contracted_plan.plan_storage_limit\n total_consumed = contracted_plan.total_consumed\n # ...\n<\/code><\/pre>\n
#plan_consumption_summary<\/code> method. We can finally include another significant piece of information, one that is mostly unfeasible to describe using just the programming language lexicon: the context.<\/p>\n
plan_consumption_summary<\/code> method is important so that users have access to what has already been consumed by them (since they could run out of storage space), and it can be used on pages that present data on plan consumption.\n<\/p><\/blockquote>\n
# Public: A summary of how much some user has consumed in a certain plan.\n#\n# This can be used whenever the information about a contracted plan is\n# presented to the its user. This way, they can get a quick overview\n# regarding the current state of the plan usage.\n#\n# contracted_plan - A ContractedPlan instance.\n#\n# Examples\n#\n# plan_consumption_summary(contracted_plan)\n# # => '2.44% (500 MB of 20 GB)'\n#\n# Returns a String.\ndef plan_consumption_summary(contracted_plan)\n total_contracted = contracted_plan.plan_storage_limit\n total_consumed = contracted_plan.total_consumed\n # ...\n<\/code><\/pre>\n