Guilherme Garnierhttps://blog.guilhermegarnier.com/
My random thoughts on softwareThu, 04 Oct 2018 11:55:25 +0000The anatomy of a code review | Guilherme Garnierhttps://blog.guilhermegarnier.com/images/ant-anatomy.pngThe anatomy of a code reviewhttps://blog.guilhermegarnier.com/2018/05/the-anatomy-of-a-code-review/
https://blog.guilhermegarnier.com/2018/05/the-anatomy-of-a-code-review/
https://blog.guilhermegarnier.com/2018/05/the-anatomy-of-a-code-review/<p>Code review is an amazing tool to improve code quality. It brings many benefits, to the reviewer, to the reviewee and to the team.</p>
<h2 id="how-we-do-code-reviews">How we do code reviews</h2>
<p>To add code reviews to your team workflow, first you need to use a version control tool. If you don’t, start with that.</p>
<p>Basically, the idea is never committing directly to the master branch - or whatever other branch you choose as the main one, but usually it’s the master branch. To start any changes to the source code, be it a new feature, refactorings or bugfixes, you need to create a new branch. You can also create conventions for the branch names, but this is not required.</p>
<p>After you finish working on your own branch, you can trigger the code review process. If you use a repository manager like <a href="https://github.com/">GitHub</a> or <a href="https://about.gitlab.com/">GitLab</a>, they already provide tools to help on this process: GitHub calls them pull requests, and GitLab, merge requests - to make it simpler, I’ll call them change requests from now on. You can add some comments explaining the purpose of the proposed changes, and maybe links to other related change requests or issues. These are very important to make the objective of that change request clear for everyone that might review your code. When in doubt, be as specific and clear as you can. Here’s an example from <a href="https://github.com/tsuru/tsuru">tsuru</a>, the project I currently work in: <a href="https://github.com/tsuru/tsuru/pull/1889">a pull request with a detailed description</a>.</p>
<p><a href="https://github.com/tsuru/tsuru/pull/1889" target="_blank">
<img src="/images/tsuru-pr-example1.png" alt="A pull request with a detailed description" style="width: 700px" />
</a></p>
<p>As soon as a change request is created, it’s available for any other member of the team to start reviewing it. You can also assign it to a specific person, if you think his opinion is important - maybe when you change a specific and obscure part of the project that not everyone dominates.</p>
<p>As a reviewer, you job is to check many aspects of the code. Here’s a non exaustive reference list:</p>
<ul>
<li><strong>accuracy and completeness:</strong> does the code do what it’s supposed to do?</li>
<li><strong>bug free:</strong> doesn’t it introduce new bugs (like not handling corner cases)?</li>
<li><strong>expressiveness:</strong> is it clear on its intentions? Can a reader clearly understand the coder’s objectives?</li>
<li><strong>scalability:</strong> could it handle the expected load?</li>
<li><strong>conventions and standards:</strong> does it follow the agreed conventions for code style, file and directory structure, etc?</li>
<li><strong>tests:</strong> do the tests cover all the use cases, including important corner cases? Is there a specific use case that deserves more tests?</li>
</ul>
<p>After checking the code, the reviewer can basically accept or reject it - GitHub also allows you to add review comments without explicitly approving or rejecting. When you approve, that means you think those changes could be promptly merged to the main branch. But when you reject, that could mean you doesn’t agree with that change at all (e.g. you don’t think this software should have the proposed feature), or that you’re requesting some changes to the code. That’s what review comments are meant for.</p>
<p>Usually a review comment can be added to a specific line or code block. That could be a typo, a function you think should be named differently, a bug you spotted or a missing test or documentation. The comments should make this clear to the reviewee, who can reply to the comments or make requested changes. Then, this process repeats until the change request gets approved.</p>
<p>When the reviewer accepts the proposed changes, the reviewee has two options: merge/rebase the changes to the main branch and finish the code review process, or ask for another developer review. This is the case when the change is very complex and you’re insecure you may be missing something. When this happens, the review process doesn’t finish until all reviewers accept the changes.</p>
<p>To illustrate, here’s another example of code review from <a href="https://github.com/tsuru/tsuru">tsuru</a>: in <a href="https://github.com/tsuru/tsuru/pull/1881">this pull request</a>, I received a couple of comments with request changes. For most of them I changed the code according the suggestions, and <a href="https://github.com/tsuru/tsuru/pull/1881#pullrequestreview-82810505">one of them</a> generated a small discussion. What’s best is that the discussion is documented in the pull request, for anyone to read and participate.</p>
<p><a href="https://github.com/tsuru/tsuru/pull/1881#pullrequestreview-82810505" target="_blank">
<img src="/images/tsuru-pr-example2.png" alt="A pull request discussion" style="width: 700px" />
</a></p>
<h2 id="benefits-of-the-code-review">Benefits of the code review</h2>
<p>A code review process brings a lot of benefits to the reviewer, to the reviewee and to the team as a whole:</p>
<h3 id="for-the-reviewer">For the reviewer</h3>
<ul>
<li><strong>learn about the changes:</strong> if someone commits directly on the main branch, only the author would know the details about it. When you review, you also get to know what those changes do, how and why they were written. And if you don’t understand, you have the opportunity to ask for more information</li>
<li><strong>learn about the project:</strong> if you are new in the team, code review is a great tool to obtain knowledge about the project. Even if you still don’t understand the project, go ahead and review everyone’s code, just to start understanding it a little better</li>
<li><strong>learn about the technologies involved (languages, frameworks, libraries):</strong> just like when you’re new to the team, if you are getting started with the tech stack used in the project, you’ll benefit even more from reviewing people’s code. You’ll learn about the language’s features, new libraries to solve common problems and features of the frameworks the project uses</li>
<li><strong>develop a critical view of someone else’s code:</strong> if you are an inexperienced developer, you probably have a hard time trying to spot problems in someone else’s code. After code reviewing a couple of times (and checking other developer’s reviews), you’ll learn how to be more critic. And what’s more important, you’ll learn that criticizing someone else’s code is not offensive, it’s actually a good thing, an opportunity for them to learn. And you’ll learn with the other’s mistakes. <a href="https://pagefault.blog/2018/04/08/why-junior-devs-should-review-seniors-commits/">That’s why junior devs should review seniors’ commits</a></li>
</ul>
<h3 id="for-the-reviewee">For the reviewee</h3>
<ul>
<li><strong>learn about the project:</strong> just like the reviewer, a great way to start in a new project is asking for feedback from the more experienced in the team. Here’s <a href="https://github.com/tsuru/tsuru/pull/1973">another example</a></li>
<li><strong>learn about the technologies involved (languages, frameworks, libraries):</strong> the same comments from the reviewer part are worth here. Here’s <a href="https://github.com/tsuru/tsuru/pull/1969#discussion_r175858044">one more example</a></li>
<li><strong>learn other ways to solve problems:</strong> when you face a bug, you may already have the complete solution in your head; that solution could really work after you implement it. But that doesn’t mean this is the only way to solve the problem. Probably there are other solutions out there, and they may be simpler, clearer, safer or perform better. Other developers could show these other solutions in their reviews</li>
<li><strong>learn to accept critics:</strong> sometimes we get so attached to the code we wrote that we could get offended when someone criticizes it. Code reviews help us learn to get over this, because we are explicitly asking for people to give feedback on our code, and they’ll answer. Over time you’ll learn these feedbacks are great ways of learning</li>
</ul>
<h3 id="for-the-team">For the team</h3>
<ul>
<li><strong>shared code ownership:</strong> now two or more people are responsible for the code one of them wrote - by the way, that could also be reached with pair programming. Shared code ownership is great after a bug is released into production: the developer who wrote the code is no longer the only responsible for that. This also helps reaching a <a href="https://medium.com/zendesk-engineering/blameless-culture-21662ab9118c">blameless culture</a></li>
<li><strong>helps keeping standards:</strong> if the team agreed on specific conventions, like code style rules, tests for every line of code or document all the APIs, code review is a great way to make people monitor each other on these</li>
</ul>
<h2 id="common-problems-and-concerns">Common problems and concerns</h2>
<h3 id="wont-this-slow-me-down">Won’t this slow me down?</h3>
<p>When we suggest that our team starts working with code reviews, people usually get apprehensive about this process making the team slower. If they’re used to finish their work, push the code and deploy to production, they may have this concern.</p>
<p>In fact, you won’t have that fast dev-to-production cycle. But that is a small downside against the many benefits presented above. It’s a trade-off, like every decision you make in your project.</p>
<p>One common concern is having to wait for a long time for another dev to start the code review process. If people still don’t see value in this process, they indeed may not prioritize this. But as soon as you get to be on the other side - waiting for other people’s reviews -, you’ll start giving it more attention. It’s an organic process that usually regulates itself.</p>
<h3 id="how-to-handle-large-change-requests">How to handle large change requests?</h3>
<p>Another problem is when the change request is very large. This will require more time from the reviewers, and it may be harder to analyze: you may lose focus during the review.</p>
<p>Just like on the previous item, this should be self regulated. If you start making very large change requests, you’ll learn they aren’t much productive when someone else asks for you to review a large change request.</p>
<p>There isn’t an ideal size of change request. But you should make them as small as possible. If you’re working on a complex feature, you may try splitting it in a couple of small change requests, independent from each other. But if you can’t make them independent, an alternative is starting with a branch forked from the main one - you may call it <code class="highlighter-rouge">feature-something</code>, then creating other branches from it. As you finish each part, make the change requests to merge to you feature branch, not the main branch. And only when you finish every “sub-feature” change request, only then you merge your feature branch to the main one.</p>
<h3 id="when-not-to-use-code-reviews">When not to use code reviews?</h3>
<p>Finally, another common question is: are there exceptions to the “never commit to the main branch” rule? If the change is very simple and small, won’t the code review process be just a formality?</p>
<p>In fact, there are a couple of situations where you may bypass the code review process. One example is updating a dependency version. But I still think it’s worth opening the change request, to make other team members aware of the changes. But this kind of decision is up to the team, and should be accorded among them.</p>
<p>To finish, I suggest reading this great post about <a href="https://insidedigitalocean.com/how-to-conduct-effective-code-reviews-18b823a07123">how to conduct effective code reviews</a>.</p>
Tue, 08 May 2018 00:00:00 +0000Increasing productivity in tmux with a single prefix key | Guilherme Garnierhttps://blog.guilhermegarnier.com/images/keyboard.jpgIncreasing productivity in tmux with a single prefix keyhttps://blog.guilhermegarnier.com/2017/12/increasing-productivity-in-tmux-with-a-single-prefix-key/
https://blog.guilhermegarnier.com/2017/12/increasing-productivity-in-tmux-with-a-single-prefix-key/
https://blog.guilhermegarnier.com/2017/12/increasing-productivity-in-tmux-with-a-single-prefix-key/<p><a href="https://github.com/tmux/tmux">Tmux</a> is a fantastic tool for improving productivity when working with a terminal. One of the first things people configure when start using tmux is changing the prefix key. The default value is <code class="highlighter-rouge">control+b</code>, which is not very confortable to press with a single hand. And as you’ll end up pressing it a lot, for every tmux command, the most common used configuration is changing it to <code class="highlighter-rouge">control+a</code>.</p>
<p>This is much better, but you still need to press two keys simultaneously before typing any tmux command. After using this configuration for some time, I decided to change it to a single key, to make it even easier.</p>
<p>I though about changing the prefix to <code class="highlighter-rouge">caps lock</code>. Besides being rarely used, it’s very well positioned. However, you can’t set <code class="highlighter-rouge">caps lock</code> as prefix in tmux. An alternative solution is mapping the <code class="highlighter-rouge">caps lock</code> key to something else. In OSX, you can set it to another modifier key, like <code class="highlighter-rouge">control</code>, <code class="highlighter-rouge">shift</code> or <code class="highlighter-rouge">esc</code>: go to <code class="highlighter-rouge">System Preferences</code> =&gt; <code class="highlighter-rouge">Keyboard</code> =&gt; <code class="highlighter-rouge">Modifier keys</code>. First I tried mapping it to <code class="highlighter-rouge">esc</code>, and setting <code class="highlighter-rouge">esc</code> as tmux prefix. It works, but this setup brought another problem: as a vim user, I use the <code class="highlighter-rouge">esc</code> key a lot (to alternate between vim modes), so now I had to type <code class="highlighter-rouge">esc</code>/<code class="highlighter-rouge">caps lock</code> twice to send the <code class="highlighter-rouge">esc</code> key to vim. It was ok, but not ideal.</p>
<p>Then I tried another solution: I installed <a href="https://github.com/tekezo/Karabiner-Elements">Karabiner-Elements</a>, a Mac app which allows you to completely customize your keyboard. So I mapped the <code class="highlighter-rouge">caps lock</code> key to <code class="highlighter-rouge">Home</code> (which doesn’t exist in Mac keyboard), and changed tmux prefix key to <code class="highlighter-rouge">Home</code>:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">set</span> <span class="nt">-g</span> prefix Home
unbind C-b
bind-key Home send-prefix
</code></pre></div></div>
<p>Now I have a great configuration: I use a single key (<code class="highlighter-rouge">caps lock</code>) as prefix, and without losing any key functionality.</p>
<p><strong>UPDATE:</strong> to do this same configuration in Linux, you just need to open the <code class="highlighter-rouge">/usr/share/X11/xkb/symbols/pc</code> file and change the line that starts with <code class="highlighter-rouge">key &lt;CAPS&gt;</code> to this:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>key &lt;CAPS&gt; <span class="o">{</span> <span class="o">[</span> Home <span class="o">]</span> <span class="o">}</span><span class="p">;</span>
</code></pre></div></div>
Tue, 05 Dec 2017 00:00:00 +0000Cheap and reliable archiving with Amazon Glacier | Guilherme Garnierhttps://blog.guilhermegarnier.com/images/glacier.jpgCheap and reliable archiving with Amazon Glacierhttps://blog.guilhermegarnier.com/2017/07/cheap-and-reliable-archiving-with-amazon-glacier/
https://blog.guilhermegarnier.com/2017/07/cheap-and-reliable-archiving-with-amazon-glacier/
https://blog.guilhermegarnier.com/2017/07/cheap-and-reliable-archiving-with-amazon-glacier/<p>Incidents like <a href="https://en.wikipedia.org/wiki/WannaCry_ransomware_attack">WannaCry ransomware</a> expose the importance of doing backups, which is usually forgotten by many people.</p>
<p>When someone talks about backing up our personal files, we usually think about services like <a href="https://www.dropbox.com">Dropbox</a> and <a href="https://drive.google.com">Google Drive</a>. But they have a reasonable cost if you have more than a couple of GB of data. There are a lot of other solutions available. Most of them are cheaper, but not always reliable - imagine if you backup all your personal data to a small and unknown service, and a few months later, the company breaks. Or a security flaw exposes all your personal data! Of course this could also happen with Dropbox and Google Drive, but it’s much less likely, being two large and serious companies.</p>
<p>One alternative to them is <a href="https://aws.amazon.com/glacier/">Amazon Glacier</a>. It’s a not so popular Amazon service for data archiving. You should notice it works differently from the usual backup solutions. When you sign up to Dropbox, for instance, you can install an app to your computer or mobile phone, or use the web interface to instantly access your files and upload new ones. Glacier is much more low level. It doesn’t have a web interface, app or even command line tool! There’s only an API, which you use to check your files, download or upload.</p>
<p>And there’s more: the download and upload rates are very slow. And to download a file, you first have to ask for a file retrieval job; the download will be available in a couple of hours!!!</p>
<p>This seems like a terrible service, so why use it? Because it’s very, very cheap! You only pay US$ 0.004 per GB per month for storage, besides <a href="https://aws.amazon.com/glacier/pricing/">additional costs</a> for requests. And even being slow and hard to use, it’s a service offered by Amazon, which gives you confidence it won’t suddenly disappear.</p>
<p>Having said that, Glacier isn’t a service to keep data you may need immediately. But it’s ideal for something you probably won’t need to access anytime soon. Think about your family pictures: when you want to access them, you probably doesn’t need them right away; you’re fine waiting a couple of hours for that.</p>
<p>Glacier is also a great option for “backups of backups”. If you want to be neurotic about backups (and you should!), you can archive a copy of your backups there.</p>
<h2 id="usage">Usage</h2>
<p>The easiest way to use Glacier is with a third party client. I like <a href="https://github.com/uskudnik/amazon-glacier-cmd-interface">amazon-glacier-cmd-interface</a>. After setting up the basic configuration, you can create a vault and upload you files:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ glacier-cmd mkvault my-disaster-backup
$ glacier-cmd upload my-disaster-backup my-file1 my-file2 ...
</code></pre></div></div>
<p>To list archives in a vault:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ glacier-cmd inventory &lt;vaultname&gt;
</code></pre></div></div>
<p>The inventory retrieval job takes a couple of hours to be processed. You can check its status with:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ glacier-cmd listjobs &lt;vaultname&gt;
+------------------------------------------------------+---------------+--------------+--------------------+--------------------------+------------+
| VaultARN | Job ID | Archive ID | Action | Initiated | Status |
+------------------------------------------------------+---------------+--------------+--------------------+--------------------------+------------+
| arn:aws:glacier:us-west-2:483413266890:vaults/backup | QYqdvM4k8q... | None | InventoryRetrieval | 2017-07-24T15:47:48.310Z | InProgress |
+------------------------------------------------------+---------------+--------------+--------------------+--------------------------+------------+
</code></pre></div></div>
<p>When the job status change to <code class="highlighter-rouge">Succeeded</code>, run the inventory command again to check the archive list.</p>
<p>To download an archive, first you need to check its id in the inventory:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ glacier-cmd inventory &lt;vaultname&gt;
Inventory of vault: arn:aws:glacier:us-west-2:483413266890:vaults/backup
Inventory Date: 2017-07-05T11:22:15Z
Content:
+---------------+---------------------+----------------------+------------------+------------+
| Archive ID | Archive Description | Uploaded | SHA256 tree hash | Size |
+---------------+---------------------+----------------------+------------------+------------+
| uFg2FE_guu... | file1.tar.gz | 2017-03-31T14:29:17Z | b41922e1a2... | 1342622251 |
| 43Wjk63Dcu... | file2.tar.gz | 2017-03-31T17:18:28Z | 2346170d22... | 2347810677 |
+---------------+---------------------+----------------------+------------------+------------+
This vault contains 2 items, total size 2.5 GB.
</code></pre></div></div>
<p>Then, create an archive retrieval job using the archive id:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ glacier-cmd getarchive &lt;vaultname&gt; &lt;archive id&gt;
+-----------+---------------+
| Header | Value |
+-----------+---------------+
| JobId | Xa17IAadQG... |
| RequestId | cPcomv_vTf... |
+-----------+---------------+
</code></pre></div></div>
<p>When the download is available (you can check its status with <code class="highlighter-rouge">glacier-cmd listjobs &lt;vaultname&gt;</code>), download it with:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ glacier-cmd download &lt;vaultname&gt; &lt;archive id&gt; --outfile &lt;filename&gt;
</code></pre></div></div>
Wed, 12 Jul 2017 00:00:00 +0000SporTV Play front-end architecture for the Olympic Games | Guilherme Garnierhttps://blog.guilhermegarnier.com/images/bff.pngSporTV Play front-end architecture for the Olympic Gameshttps://blog.guilhermegarnier.com/2017/04/sportv_play_front_end_architecture_for_the_olympic_games/
https://blog.guilhermegarnier.com/2017/04/sportv_play_front_end_architecture_for_the_olympic_games/
https://blog.guilhermegarnier.com/2017/04/sportv_play_front_end_architecture_for_the_olympic_games/<p><a href="http://globosatplay.globo.com/">Globosat Play</a> is a video product for pay TV subscribers, where you can catch up programs you missed on TV. It’s like an umbrella for a couple of different channels. One of the most popular of them is <a href="http://sportv.globo.com/">SporTV</a>, one of the largest sports channels in Brazil.</p>
<p>Last year we had the Olympic Games, a major sports event here in Rio. As SporTV channel was going to have a large coverage for the event, we decided to rethink the user experience for <a href="http://globosatplay.globo.com/sportv/">SporTV Play</a> (SporTV channel offers inside Globosat Play). In this case, we were going to focus in improving the live TV experience, which is responsible for most of the audience.</p>
<p>Besides rethinking the user experience, we decided to also rethink our front-end architecture, and address a couple of the issues we had.</p>
<p>I’ve already written about <a href="/2016/09/globosat-play-architecture-overview/">Globosat Play architecture</a> before. Its front-end is basically a couple of Rails apps using regular erb templates. These apps share components through a component library called globotv-ui. It’s about 4 years old, way before newer component technologies arised and got popular.</p>
<p>As descripted in <a href="/2016/09/globosat-play-architecture-overview/">the previous post</a>, this component library solution allowed us to avoid rewriting the same components again and again for each app. We were able to share our components, but the main problem is that this was an in-house solution. We defined our components structure to attend our needs, so it was really hard to share them outside our product.</p>
<p>Also, we had a few problems with our front-end architecture. When we updated a component that was used on pages served by different apps, we needed to “synchronize” the deploys. If we deployed one app and took too long before deploying another, in the mean time, both would have different versions of that component. That could generate an inconsistency for our product, and it was one of the issues we wanted to address with the new solution.</p>
<p>In late 2015, we started a couple of technical discussions and some proofs of concept, and decided to adopt <a href="https://facebook.github.io/react/">React</a> in our front-end. A couple of arguments led the way to our decision:</p>
<h3 id="standardize-components-structure">Standardize components structure</h3>
<p>Even after creating a dozen components in globotv-ui, we still could find some differences among them. That’s because the structure is loose and not well documented. Usually we start a new component looking at another one, and replicate that structure. But a lot of different developers worked on that library, each one with his own preferences. So we really didn’t have a pattern for components. They were well organized and tested, but in the end, they were just a couple of JS and CSS files, sometimes with a template for generating the HTML (in <a href="http://handlebarsjs.com/">Handlebars</a> for client-side rendering, or ERB templates with Rails helpers for server-side).</p>
<p>The clear and well known React component structure helps keeping a pattern among components. The <a href="https://facebook.github.io/react/docs/react-component.html#the-component-lifecycle">component lifecycle</a> lets us manage how our components should behave. It allows us to not only open source a couple of generic components, but also search for ready components instead of recreating everything we needed.</p>
<p>As an example of this, we wanted to keep our header sticky after the user scrolls down the page. Instead of implementing this behavior, we used <a href="https://github.com/KyleAMathews/react-headroom">react-headroom</a>. Problem solved!</p>
<h3 id="declarative-programming-model">Declarative programming model</h3>
<p>Another benefit that React brings is its declarative programming model, instead of the traditional imperative model. <a href="http://chibicode.com/react-js-introduction-for-people-who-know-just-enough-jquery-to-get-by/">Here</a> is a simple example: a text area with a “Tweet” button, which should be disabled while the text area field is empty. Here is the imperative implementation using jQuery:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Initially disable the button</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"button"</span><span class="p">).</span><span class="nx">prop</span><span class="p">(</span><span class="s2">"disabled"</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
<span class="c1">// When the value of the text area changes...</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"textarea"</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s2">"input"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// If there's at least one character...</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">val</span><span class="p">().</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Enable the button.</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"button"</span><span class="p">).</span><span class="nx">prop</span><span class="p">(</span><span class="s2">"disabled"</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// Else, disable the button.</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"button"</span><span class="p">).</span><span class="nx">prop</span><span class="p">(</span><span class="s2">"disabled"</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>
<p>Now the React version:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">TweetBox</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">text</span><span class="p">:</span> <span class="s2">""</span>
<span class="p">}</span>
<span class="nx">handleChange</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="na">text</span><span class="p">:</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span> <span class="p">})</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span>
<span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">textarea</span> <span class="nx">onChange</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">}</span><span class="o">&gt;&lt;</span><span class="sr">/textarea</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="nx">button</span> <span class="nx">disabled</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">text</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">Tweet</span><span class="o">&lt;</span><span class="sr">/button</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="globo-play-release">Globo Play release</h3>
<p>The third strong argument in favor of React was the release of <a href="https://globoplay.globo.com/">Globo Play</a>, in late 2015. It’s another video product developed here at <a href="http://www.globo.com/">Globo.com</a>. It’s very similar to Globosat Play, and it already used React. So when we started developing the new interface for SporTV Play at the Olympic Games, the team that developed Globo Play already had a great experience with it to help our adoption.</p>
<h3 id="the-new-architecture">The new architecture</h3>
<p>As descripted in <a href="/2016/09/globosat-play-architecture-overview/">a previous post</a>, our architecture was already microservices-based:</p>
<figure style="text-align: center">
<img src="/images/globotv-architecture2.png" alt="Globosat Play original architecture" style="width: 800px" />
<figcaption>Globosat Play original architecture</figcaption>
</figure>
<p>In the front-end, we had a couple of different apps to serve different pages in our product, with an <a href="http://nginx.org/">nginx</a> server in front of them. The nginx server proxies the requests to each app, according to the request path. Our idea was adding a new rule to it, to forward all requests to SporTV Play home and live signals pages to a new app. This new project was going to use React, and ideally share components with Globo Play.</p>
<p>The first step was thinking about the API for this new front-end app. We already had a couple of APIs serving our current apps, so we didn’t need to create a new API. We could just use what we already had, but we decided to follow the <a href="http://samnewman.io/patterns/architectural/bff/">Back-end for Front-end pattern</a>.</p>
<p>The APIs we already had served well many of our apps, and some of them are legacy. They have a lot of services, and most of them are fine-grained. To serve the new front-end app, we would probably need to make many requests to group all the data we needed.</p>
<p>Also, we thought it was a good idea to separate the new app from the old ones. Doing this, we would have less chance of coupling old and new apps. Suppose both of them consumed the same services; that would create a coupling between them. If the new app required a change in this service contract, we wouldn’t be able to do that without the risk of breaking the old app.</p>
<p>Besides that, we know the mobile consumption is raising, and sometimes we suffer with terrible 3/4g connections. With a new API specifically designed to attend the needs of the new app, we could deliver the smallest possible payload (just the data we were going to use). Also, we could create more specific services, reducing the number of requests to a minimum.</p>
<p>We followed two principles from the Back-end for Front-end (BFF) pattern: the front-end consumes services from its BFF and nothing more; and the front-end is the only BFF client. They are tightly coupled together, but this is not a problem, because they should both be maintained by the same team. It’s like we are splitting our app in two. The BFF is responsible for orchestrating requests from internal, fine-grained services, apply some business rules and deliver data ready to be consumed by the front-end, which just consumes these coase-grained services and takes care of the presentation layer.</p>
<figure style="text-align: center">
<img src="/images/bff.png" alt="BFF architecture" style="width: 800px" />
<figcaption>BFF architecture</figcaption>
</figure>
<p>One downside of the BFF is code replication. Some of the services we created in the BFF were new and very specific to our new SporTV Play front-end app (like the current schedule and the list of live signals). Others were already available in our internal APIs. But to follow the rule were the front-end can’t access any service outside its BFF, we needed to add a new route to the BFF, and basically make a proxy pass to our internal APIs. As any decision in software engineering, it’s a tradeoff. There is no perfect solution for everything, and the benefits overcome this issue.</p>
<p>For more details and discussions about the BFF pattern, check out <a href="http://samnewman.io/patterns/architectural/bff/">Sam Newman</a>’s and <a href="http://philcalcado.com/2015/09/18/the_back_end_for_front_end_pattern_bff.html">Phil Calçado</a>’s articles.</p>
<p>This is the first post of a series. In the next ones, I intend to write a little more about React components, state management, CSS architecture and components sharing.</p>
Tue, 18 Apr 2017 00:00:00 +0000Handling request cache in Ruby | Guilherme Garnierhttps://blog.guilhermegarnier.com/2017/02/handling_request_cache_in_ruby/
https://blog.guilhermegarnier.com/2017/02/handling_request_cache_in_ruby/<p>Handling HTTP cache is one of the most important aspects when you need to scale a web application. If well used, it can be your best friend; but when badly used, it may be you worst enemy.</p>
<p>I’m not going to explain the basic aspects of caching here, there are already a lot of great material about it. I’m going to bring a specific problem here.</p>
<p>On a previous post, I wrote about Globosat Play architecture. As explained there, it evolved to a microservices architecture, and as such, we ended up making a lot of HTTP requests to our internal services. So we needed to manage those requests very well.</p>
<p>Suppose you access a page like <a href="http://globosatplay.globo.com/combate/">Combate channel home page</a>. To fill up every information in that page, we need to query data from:</p>
<ul>
<li>a videos API, to bring up a list of available channels and the latest videos from Combate channel</li>
<li>a highlights API, to check the latest highlights selected by an editor</li>
<li>an events API, to check a list of previous and next UFC events</li>
</ul>
<p>That means a user request could be represented by something like this:</p>
<figure style="text-align: center">
<img src="/images/requests-without-cache.png" alt="Requests without cache" style="width: 500px" />
<figcaption>Requests without cache</figcaption>
</figure>
<p>Now imagine one of these services is unavailable. Or very slow. Or giving un unexpected answer. If we didn’t consider these scenarios, we would end up with a brittle application, susceptible to a lot of issues.</p>
<p>Michael Nygard, in <a href="https://pragprog.com/book/mnee/release-it">Release It!</a>, says we must develop cynical systems:</p>
<blockquote>
<p>Enterprise software must be cynical. Cynical software expects bad things to happen and is never surprised when they do. Cynical software doesn’t even trust itself, so it puts up internal barriers to protect itself from failures. It refuses to get too intimate with other systems, because it could get hurt.</p>
</blockquote>
<p>That means we shouldn’t trust anybody. Don’t assume a service is up, available, fast and correct. Even if you know and trust the maintainers of this service, consider that it may have problems (and it will, eventually!). One of the defense mechanisms against this is caching.</p>
<p>At Globosat Play, we decided to implement two levels of cache. We call them performance and stale.</p>
<h2 id="performance-cache">Performance cache</h2>
<p>The performance cache is meant to avoid a flood of unnecessary requests to a single resource in a short period of time. Going back to Combate home page example, one of the services our back-end requests is a list of next UFC events. This doesn’t change often; only when a new event is created, or when an event finishes, once a couple of weeks. That means, it’s very wasteful to hit that service for every user accessing Combate home page. Suppose the events API response changes once a week; if that page gets 100,000 hits in that period, that means I would make 100,000 requests for that API, when I could just make one and keep the results in cache, which is much faster.</p>
<p>The solution for this is keeping a performance cache for a specific period of time. Suppose I set my cache for 5 minutes. The decision flow for this would be:</p>
<ul>
<li><strong>cache available?</strong> Respond with cache</li>
<li><strong>cache unavailable?</strong> Make the request, write the response in cache, set its TTL (Time-to-leave) for 5 minutes, respond</li>
</ul>
<p>That means I would hit that API only once every 5 minutes, independenly of how many users are accessing my home page right now. I’m not only avoiding wasteful requests, but also protecting my internal services and giving faster responses - it’s much faster to access the cache than making an HTTP request. The diagram below depicts this scenario:</p>
<figure style="text-align: center">
<img src="/images/requests-with-cache.png" alt="Requests with cache" style="width: 500px" />
<figcaption>Requests with cache</figcaption>
</figure>
<p>The problem in this scenario is, even if I’m sure that my events API only changes once a week, I can’t set my cache TTL for 1 week. Imagine if I do that and the cache expires a few minutes before a new event is registered. That means I won’t see the new event until the next week! You need to carefully evaluate the performance cache times for each service you depend on.</p>
<p>Even if you have a service that can’t be cached for that long, you could have a great benefit from caching the request for at least a few seconds. Imagine an application with 10,000 requests/s. If you set the back-end service request cache TTL for 1 second, you are making a single request for your service, instead of 10,000 requests!</p>
<h2 id="stale-cache">Stale cache</h2>
<p>The second cache level is stale. It’s a safety against problems like network instability or service unavailable. Let’s use the latest videos API as an example. Suppose my application back-end tries to access this service and it gets a 500 HTTP status code. If I have a stale cached version of it, I can use it to give a valid response to its client. The stale data may be outdated by a few minutes or hours, but it’s still better than giving no response at all - of course, that depends on the case. For some kinds of services, an outdated response may not be feasible, like giving the wrong balance when your client accesses his bank account. But for most of the cases, stale cache is a great alternative.</p>
<p>Usually we set the performance cache time for a few minutes and the stale cache for a few hours. Our standard setup is 5 minutes and 6 hours, respectivelly.</p>
<h2 id="implementing-cache-levels-in-ruby">Implementing cache levels in Ruby</h2>
<p>To implement performance and stale cache levels in Ruby applications, we created and open sourced a gem called <a href="https://github.com/globocom/content-gateway-ruby">Content Gateway</a>. With it, it’s much easier to manage cache levels.</p>
<p>After installing it, you need to configure the default request timeout, the performance and stale cache expiration times and the cache backend, besides other optional configurations:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">config</span> <span class="o">=</span> <span class="no">OpenStruct</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span>
<span class="ss">timeout: </span><span class="mi">2</span><span class="p">.</span><span class="nf">seconds</span><span class="p">,</span>
<span class="ss">cache_expires_in: </span><span class="mi">5</span><span class="p">.</span><span class="nf">minutes</span><span class="p">,</span>
<span class="ss">cache_stale_expires_in: </span><span class="mi">6</span><span class="p">.</span><span class="nf">hours</span><span class="p">,</span>
<span class="ss">cache: </span><span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Cache</span><span class="p">.</span><span class="nf">lookup_store</span><span class="p">(</span><span class="ss">:memory_store</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">gateway</span> <span class="o">=</span> <span class="no">ContentGateway</span><span class="o">::</span><span class="no">Gateway</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"My API"</span><span class="p">,</span> <span class="n">config</span><span class="p">)</span>
</code></pre></div></div>
<p>With this basic configuration, you can start to make HTTP requests. You can also override the default configurations for each request:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Params are added via query string</span>
<span class="n">gateway</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="s2">"https://www.goodreads.com/search.xml"</span><span class="p">,</span> <span class="ss">key: </span><span class="no">YOUR_KEY</span><span class="p">,</span> <span class="ss">q: </span><span class="s2">"Ender's Game"</span><span class="p">)</span> <span class="c1"># =&gt; "&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;GoodreadsResponse&gt;\n &lt;Request&gt;..."</span>
<span class="c1"># Specific configuration params are supported, like "timeout" and "skip_cache"</span>
<span class="n">gateway</span><span class="p">.</span><span class="nf">get_json</span><span class="p">(</span><span class="s2">"https://api.cdnjs.com/libraries/jquery"</span><span class="p">,</span> <span class="ss">timeout: </span><span class="mi">1</span><span class="p">.</span><span class="nf">second</span><span class="p">,</span> <span class="ss">skip_cache: </span><span class="kp">true</span><span class="p">)</span> <span class="c1"># =&gt; {"name"=&gt;"jquery", "filename"=&gt;"jquery.min.js", "version"=&gt;"3.1.1", ...</span>
</code></pre></div></div>
<p>It supports POST, PUT and DELETE as well. For all verbs, there are two methods for making the request: one is simply the name of the verb and the other has <code class="highlighter-rouge">_json</code> suffix. The former treats the response body as string, and the latter, as a Hash.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gateway</span><span class="p">.</span><span class="nf">post_json</span><span class="p">(</span><span class="s2">"https://api.dropboxapi.com/2/files/copy"</span><span class="p">,</span> <span class="ss">headers: </span><span class="p">{</span> <span class="no">Authorization</span><span class="p">:</span> <span class="s2">"Bearer ACCESS_TOKEN"</span> <span class="p">},</span> <span class="ss">payload: </span><span class="p">{</span> <span class="ss">from_path: </span><span class="s2">"path1"</span><span class="p">,</span> <span class="ss">to_path: </span><span class="s2">"path2"</span> <span class="p">})</span>
<span class="n">gateway</span><span class="p">.</span><span class="nf">put_json</span><span class="p">(</span><span class="s2">"https://a.wunderlist.com/api/v1/list_positions/id"</span><span class="p">,</span> <span class="ss">payload: </span><span class="p">{</span> <span class="ss">values: </span><span class="p">[</span><span class="mi">4567</span><span class="p">,</span> <span class="mi">4568</span><span class="p">,</span> <span class="mi">9876</span><span class="p">,</span> <span class="mi">234</span><span class="p">],</span> <span class="ss">revision: </span><span class="mi">123</span> <span class="p">})</span>
<span class="n">gateway</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="s2">"https://a.wunderlist.com/api/v1/tasks/id"</span><span class="p">)</span>
</code></pre></div></div>
<p>You can also make a few other customizations. Check out <a href="https://github.com/globocom/content-gateway-ruby">the project page on github</a> for more information and examples.</p>
Thu, 09 Feb 2017 00:00:00 +0000Software Developers and Digital Artists | Guilherme Garnierhttps://blog.guilhermegarnier.com/2016/12/software-developers-and-digital-artists/
https://blog.guilhermegarnier.com/2016/12/software-developers-and-digital-artists/<p><a href="https://jovemnerd.com.br/nerdcast/">Nerdcast</a> is an amazing podcast (in portuguese) about nerdie stuff in general. One of its latest episodes talked about the <a href="https://jovemnerd.com.br/nerdcast/profissao-artista-digital/">digital artist profession</a>. The guests were animators that worked on feature films like <a href="http://www.imdb.com/title/tt3521164/">Moana</a> and <a href="http://www.imdb.com/title/tt1211837/">Doctor Strange</a>. They talked a lot about what they do, and I saw a lot of analogies between their work and software development.</p>
<p>One of the podcast guests said that we was working on a specific scene for Doctor Strange for about 2 months, and suddenly his boss called him and told the director decided to cut that scene off from the film. Those 2 months of work turned into garbage. The lesson he learned from that is, you shouldn’t get attached to the project you’re working on. It’s not <strong>your project</strong>, it’s <strong>your company’s project</strong> and you happen to be working on it, which is very different. The analogy here is very clear, because that happens a lot in software projects: sometimes your client decides the feature you’ve been working on for a few weeks or months is not that important, or even worse, that the whole project shouldn’t be maintained anymore. It’s very hard not to be impacted by these news, but if you don’t work on that, you will frequently get frustrated. The project is meant to deliver value to your client, not you. Always remember that <a href="http://bravenewgeek.com/you-are-not-paid-to-write-code/">you are not paid to write code</a>.</p>
<p>If you want a project to be really yours, and have total freedom to decide what to do and how to do it, you need a personal project. But it’s important to notice that, if someday you decide to make that a business, turning that into a startup or selling some kind of product or service, you will start to have clients. When that happens, you need to be ready to let go of your ideas; if nobody wants your product, be ready to <a href="https://en.wikipedia.org/wiki/Lean_startup#Pivot">pivot</a> (or even discontinue the whole product). <a href="https://en.wikipedia.org/wiki/A/B_testing">A/B tests</a> are also a great way to learn how to let go of your ideas and beliefs: if a hypothesis is proven worse than the default behavior, just delete it.</p>
<p>Another important topic that was mentioned in the podcast was: the guests, as artists, have a hard time deciding when to stop improving their work. They start working on a scene and iterate a couple of times to make it better. As perfectionists, they want to keep polishing their work. But sometimes the scene is already looking so good that any improvement won’t be noticed by the audience, so it just <strong>won’t deliver value to the client anymore</strong>. The problem is not knowing when to stop. We can make an analogy here with refactoring: sometimes we develop a new feature, and even after it’s implemented and well tested, we decide to refactor. The objective could be making the code clearer for anyone that may touch it later - to implement a new feature of fix a bug - or maybe extracting a part of it to remove duplication from a similar feature you already had. In both cases, the refactoring won’t deliver value in the short term, but will on the mid-term or long term: you will have lower maintenance costs. But we may have the same problem as the animators: it’s very hard to know when to stop polishing the code. At some point, the refactoring won’t deliver value anymore, and we just refactor to please ourselves. As said before, you need to remember the project is not yours, and <a href="http://bravenewgeek.com/you-are-not-paid-to-write-code/">you are not paid to write code</a>!</p>
Mon, 12 Dec 2016 00:00:00 +0000Globosat Play architecture overview | Guilherme Garnierhttps://blog.guilhermegarnier.com/images/globosatplay-architecture.pngGlobosat Play architecture overviewhttps://blog.guilhermegarnier.com/2016/09/globosat-play-architecture-overview/
https://blog.guilhermegarnier.com/2016/09/globosat-play-architecture-overview/
https://blog.guilhermegarnier.com/2016/09/globosat-play-architecture-overview/<p>This post is at least one year late. Since I gave a few <a href="https://blog.guilhermegarnier.com/talks/">talks</a> about <a href="http://globosatplay.globo.com/">Globosat Play</a> architecture (<a href="https://blog.guilhermegarnier.com/evolucao-arquitetura/">slides in portuguese</a>), I intended to write a more detailed post, but always procrastinated about this.</p>
<p><a href="http://www.globo.com/">Globo.com</a> is the internet arm of the largest media conglomerate from Brazil, and one of the largest in the world. One of the areas of the company is responsible for our video platform, which includes encoding, distribution and streaming for any website of the group that needs videos.</p>
<p>About 5 years ago, one of our videos team developed a video product called <a href="https://web.archive.org/web/20151103032004/http://globotv.globo.com/">globo.tv</a> (recently discontinued and replaced with a newer product, <a href="https://globoplay.globo.com/">Globo Play</a>). Its content was focused on Rede Globo (our main broadcast TV network) shows, like news, sports and the famous brazilian telenovelas. Most of globo.tv content were small scenes from these TV shows, open for every user, but it also offered full episodes for paying subscribers.</p>
<figure style="text-align: center">
<img src="/images/globotv.png" alt="globo.tv" style="width: 800px" />
<figcaption>globo.tv home page</figcaption>
</figure>
<p>The original architecture was a single monolithic <a href="http://rubyonrails.org/">Rails</a> app, with a <a href="https://unicorn.bogomips.org/">Unicorn</a> application server, a <a href="https://www.mongodb.com/">MongoDB</a> database and a <a href="http://redis.io/">Redis</a> instance for cache, all behind an <a href="https://nginx.org/">nginx</a> HTTP server. This architecture served very well for some time.</p>
<figure style="text-align: center">
<img src="/images/globotv-architecture.png" alt="globo.tv architecture" style="width: 600px" />
<figcaption>globo.tv architecture</figcaption>
</figure>
<p>Then came 2012 with new demands for globo.tv. We needed to start offering live streaming of a couple of events, like <a href="http://www.ufc.com.br/">UFC</a>, <a href="http://gshow.globo.com/realities/bbb/">Big Brother Brasil</a>, soccer championships and the Winter Olympic Games. Also, we needed to start offering a collection of videos from <a href="http://sportv.globo.com/site/combate/">Combate</a> channel, focused on MMA sports.</p>
<p>At this point, we realized the old monolithic architecture wouldn’t serve anymore. So we needed to break it in smaller parts. The first step was identifying smaller subdomains inside videos. The first ones we identified and split were live streaming, for these new live events demand, and VoD (video on demand), for Combate channel videos.</p>
<p>Clearly these new subdomains deserved their own projects, but the problem is, they needed to share a few business rules and data from the original globo.tv project, and also between them. That meant we needed to extract a few services from globo.tv to its own project: <em>globotv-api</em> was born!</p>
<figure style="text-align: center">
<img src="/images/globotv-architecture2.png" alt="globo.tv architecture, 2.0" style="width: 800px" />
<figcaption>globo.tv architecture, 2.0</figcaption>
</figure>
<p>At this point, we had:</p>
<ul>
<li><em><strong>globotv-api</strong></em>, which offered a few basic video services, like the most recent videos from a specific program and the most watched programs</li>
<li><em><strong>globotv</strong></em>, the remain of the original project, which started to consume <em>globotv-api</em> services. It was responsible for serving the original pages, like home, program page, video page and search</li>
<li><em><strong>globotv-events</strong></em>, a new project responsible for live streaming of different kinds of events. It also consumed <em>globotv-api</em> and offered its own API with specific services, like a list of live events happening right now</li>
<li><em><strong>globotv-vod</strong></em>, another new project, which served the collection of videos for Combate channel. It also consumed <em>globotv-api</em> and offered its own API with specific services, like a list of competitions and videos from a specific fighter</li>
</ul>
<p>The break-up of the monolith was a very important move. If we didn’t split it at that time, we would end up with a larger and larger project, which would soon become a monster - harder to understand, harder to maintain, harder to evolve. It allowed us to share a small part of our domain, which was common among these new requirements and the original project.</p>
<p>To split the front-end among different projects, we used <a href="https://nginx.org/">nginx</a> as a router. We already used it in front of our application server, but with multiple projects, we created an upstream for each one, and configured a location for each URL pattern, like this:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>upstream globotv {
server globotv.internal.globo.com;
}
upstream globotv-events {
server globotv-events.internal.globo.com;
}
upstream globotv-vod {
server globotv-vod.internal.globo.com;
}
server {
listen 80;
server_name globotv.globo.com;
location ~ (.+)/ao-vivo/ {
proxy_set_header Host $http_host;
proxy_pass http://globotv-events;
break;
}
location ~ ^/combate/ {
proxy_set_header Host $http_host;
proxy_pass http://globotv-vod;
break;
}
location ~ / {
proxy_set_header Host $http_host;
proxy_pass http://globotv;
break;
}
}
</code></pre></div></div>
<p>The internal domains aren’t publicly exposed, the only way to access the projects is through nginx. With a configuration similar to this, nginx routes incoming requests for <em>globotv.globo.com</em> domain to different projects, according to the URL pattern: URLs that contain “/ao-vivo/” pattern (“live” in portuguese) are routed to <em>globotv-events</em> project; URLs starting with “/combate/” are forwarded to <em>globotv-vod</em> project. The last location matches every other URL to the original <em>globotv</em> project. <em>globotv-api</em> project doesn’t appear in this configuration, as it isn’t publicly accessible (it’s only accessed from the other projects). This configuration allowed us to serve different pages from different projects transparently for the user.</p>
<p>Despite many benefits, this architectural change brought new challenges. The first one was keeping a consistent visual identity among pages served from different projects. For example, the video thumb component used in the home page should be just like the video thumb from the search page; the product header should be the same in every page. The split in multiple projects was an architectural decision; the user doesn’t need to know this, because globo.tv was still a single product.</p>
<p>The solution for this problem was creating a components library, called <em>globotv-ui</em>. With this solution, we were able to share visual components, comprised of HTML, JS and CSS. They were standardized and documented, which made it very easy to create new components and share them among these projects - as all of them were Ruby on Rails projects, we delivered <em>globotv-ui</em> library as a Rubygem.</p>
<figure style="text-align: center">
<img src="/images/globotv-ui-components.png" alt="examples of components from globotv-ui library" style="width: 700px" />
<figcaption>examples of components from globotv-ui library</figcaption>
</figure>
<p>Fast forward a few years, and a new video product emerged: <a href="http://globosatplay.globo.com/">Globosat Play</a>. It was very similiar to globo.tv on a few aspects - the idea was offering VoD and live streaming from <a href="http://canaisglobosat.globo.com/">Globosat</a> channels (TV channels available only for paying subscribers), but with a few differences: we also needed to offer movies, and now the focus was on subscribers, and not on free users anymore.</p>
<figure style="text-align: center">
<img src="/images/globosatplay-homepage.png" alt="Globosat Play home page" style="width: 700px" />
<figcaption>Globosat Play home page</figcaption>
</figure>
<p>The main challenge at that point was how to share components and services between these two products, but without one limiting the other’s evolution and requirements. We needed to re-evaluate our architecture and business domain to solve this issue. We realized that, as both products had many similarities and some differences, we needed to create a common, shared layer of services, but also keep some services specific. The new architecture has become something like this:</p>
<figure style="text-align: center">
<img src="/images/globosatplay-architecture.png" alt="Globosat Play architecture" style="width: 800px" />
<figcaption>Globosat Play architecture</figcaption>
</figure>
<p>This diagram is simplified from the previous ones - the new projects are also Rails apps with their own MongoDB and Redis instances. The green boxes are projects that serve web pages, and the blue ones are APIs. We split our projects in 3 parts: the top one in the image is specific to globo.tv product; the bottom one is specific to Globosat Play; and the middle one represent shared services. You can notice most of the projects created before (<em>globotv-api</em>, <em>globotv-events</em> and <em>globotv-vod</em>) started being shared, as Globosat Play also had those same requirements.</p>
<p>Besides that, we created a few other projects. Some of them, like <em>movies</em>, attended a specific subdomain that didn’t exist before; others, like <em>globotv-search</em>, were extracted from the original <em>globotv</em> project - these features already existed, but now we needed to share them between globo.tv and Globosat Play. Also, <em>globotv-api</em> kept being our main source for basic video services.</p>
<p>This evolution also required a few new configurations on our nginx server:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>upstream movies {
server movies.internal.globo.com;
}
upstream globotv-search {
server globotv-search.internal.globo.com;
}
upstream globosat-play {
server globosatplay.internal.globo.com;
}
upstream globotv-events {
server globotv-events.internal.globo.com;
}
upstream globotv-vod {
server globotv-vod.internal.globo.com;
}
server {
listen 80;
server_name globosatplay.globo.com;
location ~ (.+)/ao-vivo/ {
proxy_set_header Host $http_host;
proxy_pass http://globotv-events;
break;
}
location ~ ^/telecine/ {
proxy_set_header Host $http_host;
proxy_pass http://movies;
break;
}
location ~ ^/busca/ {
proxy_set_header Host $http_host;
proxy_pass http://globotv-search;
break;
}
location ~ / {
proxy_set_header Host $http_host;
proxy_pass http://globosat-play;
break;
}
}
</code></pre></div></div>
<p>In the end, we realized the microservices architecture brought a lot of advantages:</p>
<ul>
<li><strong>smaller, easier to manage projects</strong>: each subdomain is separated in its own project, which helps keeping the code smaller and easier to understand. Also, this allows different teams to work in different projects</li>
<li><strong>faster builds</strong>: with many small projects, the time needed to build and run test suites for each one gets smaller, which gives faster feedback cycles. That stimulates developers to run the test suites more frequently</li>
<li><strong>smaller and less risky deploys</strong>: each project is responsible for a small part of the product; that means bugs only affect that small subdomain of it. Suppose you introduce a bug in the search project; that would affect only the search page. Your users would still be able to access the home page and watch videos. That gives confidence for the team to deploy to production more frequently, which reduces even more the risk of bugs</li>
<li><strong>flexible infrastructure</strong>: every service is a REST API over HTTP, using JSON format. That means you could write each one in a different language with a different database technology, if you wanted and needed. You could select the best tools for the job</li>
<li><strong>easier incremental changes</strong>: suppose you want to migrate your application server from <a href="https://unicorn.bogomips.org/">Unicorn</a> to <a href="http://puma.io/">Puma</a>. With a monolithic application, you would need to flip the switch all at once. With many small services, you could choose one to try Puma - maybe the less critical one. If the migration is successful, you could continue with this process one project at a time</li>
</ul>
<p>But we also had a few disadvantages:</p>
<ul>
<li><strong>more complex architecture</strong>: when a new developer starts in your team, it’s much harder to explain to him how the architecture works and what each project is responsible for</li>
<li><strong>harder local environment setup</strong>: another problem for new developers is setting up the local environment. With many projects, each one with its own requirements, that’s much harder</li>
<li><strong>harder to update dependencies, like newer gems</strong>: when you need to update a dependency, like a gem that fixes a critical security flaw, you need to do that once for each project. With a monolith, you would just need to do that a single time</li>
<li><strong>harder to test</strong>: when each service depends on many others, the setup for the test environment is much harder. You would need to set a complex environment for integration tests, or create fake APIs to replace real ones, or maybe mock API responses, using something like <a href="https://github.com/vcr/vcr">VCR</a></li>
<li><strong>heterogeneous environment</strong>: the flexibility earned with microservices might result in projects in different languages, with different databases and other dependencies. That makes it harder to maintain, because not every developer may understand the whole set of technologies</li>
<li><strong>more failure points, harder to debug</strong>: when a service fails, the problem may be a bug or database failure, for example, but may also be the result of a failure in another service it depends on. The debugging process gets harder and slower</li>
</ul>
<p>In the end, the migration from monolith to microservices was very successful to us. But the main point here is realizing that microservices aren’t the magical solution to every problem - a messy monolith split would generate many messy microservices, as illustrated in the image below. You should analyze the characteristics of your project before deciding if microservices are the best option.</p>
<div style="margin: 0 auto; display: table">
<blockquote class="twitter-tweet">
<p lang="en" dir="ltr">Monolithic vs Microservices
<a href="https://t.co/EFUpsYkBNu">pic.twitter.com/EFUpsYkBNu</a>
</p>
&mdash; Fuckowski (@fuckowski)
<a href="https://twitter.com/fuckowski/status/717643893655937024">April 6, 2016</a>
</blockquote>
<script async="" src="//platform.twitter.com/widgets.js"></script>
</div>
Thu, 01 Sep 2016 00:00:00 +0000Modern-world birthday statistics | Guilherme Garnierhttps://blog.guilhermegarnier.com/images/happy-birthday.pngModern-world birthday statisticshttps://blog.guilhermegarnier.com/2016/02/modern-world-birthday-statistics/
https://blog.guilhermegarnier.com/2016/02/modern-world-birthday-statistics/
https://blog.guilhermegarnier.com/2016/02/modern-world-birthday-statistics/<p>When I was younger, I used to get a lot of phone calls during my birthdays, from my relatives and friends. But now the situation is much different.</p>
<p>A few days ago I got older, and the “happy birthday” wishes came from many different mediums. So I decided to compile some curious (and completely useless) statistics about this:</p>
<p><a href="/images/happy-birthday.png" class="post-image-link"><img src="/images/happy-birthday.png" alt="Happy Birthday" /></a></p>
<p>Sometimes technology makes life more complicated, instead of simplifying it.</p>
Thu, 18 Feb 2016 00:00:00 +0000Software quality isn't optional | Guilherme Garnierhttps://blog.guilhermegarnier.com/images/fast-food.jpgSoftware quality isn't optionalhttps://blog.guilhermegarnier.com/2015/11/software-quality-isnt-optional/
https://blog.guilhermegarnier.com/2015/11/software-quality-isnt-optional/
https://blog.guilhermegarnier.com/2015/11/software-quality-isnt-optional/<p>In the last weekend, I attended to <a href="http://devday.devisland.com/">DevDay 2015</a>, in Belo Horizonte, where I presented a talk about <a href="https://blog.guilhermegarnier.com/evolucao-arquitetura/">“evolution of a distributed architecture”</a> (in portuguese).</p>
<p>The last talk of the event presented <a href="http://stackoverflow.com/">Stack Overflow</a> architecture. This talk was very controversial, and I felt I needed to write something to show my point of view on this subject.</p>
<p>A lot is being said in the community about the value of software engineering best practices. For the last decades, people like Uncle Bob, Kent Beck, Martin Fowler and many others have been doing a great effort promoting these practices, through books, posts and talks. These professionals made and still make a great job orienting new developers, so we can have well tested software projects and well designed architectures, focusing on aspects like maintainability, scalability, security and quality.</p>
<p>Despite this, we know that, in many companies, management makes pressure on developers to deliver as fast as possible, forgetting about software quality and maintainability - even knowing they will be responsible for the project maintenance, at least in short term. This habit goes against every best practice described above, and is very harmful to new developers. These ones that start their career in companies with this idea in mind, even if they’ve read about best practices, end up believing that this is utopian, and that in practice it’s impossible to design a sustainable architecture, refactor legacy code or automate tests, because of pressures for fast delivery of new features. That’s why it’s essential to spread the word about best practices, to show these new developers that it’s not only possible to focus on quality, but also essential for the project evolution and maintenance.</p>
<p>In the talk that closed <a href="http://devday.devisland.com/">DevDay 2015</a>, <a href="http://stackoverflow.com/">Stack Overflow</a> architecture was presented. The displayed numbers are impressive: it’s one of the 50 top sites in the world, with millions of page views per day, and all that supported by only 9 physical servers, each one working at around 5% load, plus 2 database servers. With this structure, the average load time is only 18 ms. How is this possible?</p>
<p>The secret, according to this talk, is their obsession with performance. Every new feature must have the best possible performance. When a library or tool they use isn’t considered fast enough, they rewrite it from scratch. As layered architectures are slow, every database query is manually written, and directly in the controller.</p>
<p>Stack Overflow code has low testability, because you can’t create mocks to replace the database connection, for instance. That’s why the project has very few automated tests (and it was mentioned that some developers don’t even run the tests). As they have a massive number of active and engaged users, any bug that’s introduced after a deploy is quickly found and reported at <a href="http://meta.stackoverflow.com/">Meta Stack Overflow</a>. To update the operating system version, they just remove a server from the pool, apply the update and put the server back live. They assume that, if a new bug arises, users will soon find and report it.</p>
<p>The speaker let it very clear that modeling, architecture and tests are good stuff, but they’re not for everyone. I personally disagree.</p>
<p>Stack Overflow is a very particular case. As their audience is made of developers, and they’re very engaged and passionate about the product, bugs in production are considered acceptable, because the main focus is performance. But what’s the point of performance without quality? Would you buy the fastest car in the world even knowing it doesn’t have seat belts and air bags, and that it doesn’t support replacing a flat tire or a defective part? The analogy is very exaggerated - a bug in the site doesn’t involve life risk, but what I mean is, if you focus only and exclusively on performance, you give up other aspects like quality and security. It’s the same line of thought from those managers I mentioned before, who make a lot of pressure for fast delivery, regardless of quality.</p>
<figure style="text-align: center">
<img src="/images/fast-food.jpg" alt="Fast food" style="width: 400px" />
<figcaption>Does every fast food need to be like this?</figcaption>
</figure>
<p>In my opinion, even if your project focuses on performance, quality can’t be abandoned. The other extreme - over-engineering - is also bad; if you have a small application, with only a couple of users, it doesn’t make sense to create a complex architecture, thinking about the possibility of maybe one day it may expand. This would be creating a solution for a problem that doesn’t exist.</p>
<p>I have a real example for this: I register all my expenses in Google Spreadsheet. I wanted to share these data with my wife, but as the spreadsheet is very large, I created a small app that extracts these data and displays a very simple dashboard, only with information that she is interested in. This app has only two users - me and her -, and there isn’t a chance that this grows up. In this case, it doesn’t make sense for me to think about a scalable architecture. But when we talk about a product with tens of millions of users, the situation is very different. Currently, Stack Overflow doesn’t have a competitor to match, but if one rises with a better user experience or new features, they will have a hard time to follow.</p>
<p>My main concern while watching this talk was the impact that these ideas can have on the audience. The majority of them were very young, probably students or professionals starting their career. A talk like this, spreading the word about optional software quality, can be very harmful to them. And it seems like <a href="http://www.darkcoding.net/software/facebooks-code-quality-problem/">Facebook has a similar issue with code quality</a>.</p>
<p>To wrap up, I want to make it clear that I’m not doing a personal attack against Stack Overflow or the speaker. I admire her courage to take the stage and present such controversial ideas, even though I disagree on them. I’m a Stack Overflow user and will continue being after all.</p>
Thu, 05 Nov 2015 00:00:00 +0000Lost in translation | Guilherme Garnierhttps://blog.guilhermegarnier.com/2015/11/lost-in-translation/
https://blog.guilhermegarnier.com/2015/11/lost-in-translation/<p>After a few years maintaining this blog, I decided to start writing in english. I feel like I can reach a wider audience with this. Sometimes when I make a comment in a blog post or github issue, or answer a question in Stack Overflow, I want to link to something I wrote in my blog, but as I wrote in portuguese, I can’t do that.</p>
<p>I don’t think this is going to be a problem for most developers - at least <a href="/2009/07/livros-tecnicos-traduzidos-nunca-mais/">I hope so</a>; if you are a software developer and don’t understand english, you should!</p>
<p>Eventually I may write something in portuguese again, if I have a reason for that, so I tagged every old post with <a href="/search/?t=portuguese">portuguese</a>. And every post in english will be tagged with <a href="/search/?t=english">english</a>. I also added direct links in the sidebar, for easy access.</p>
Thu, 05 Nov 2015 00:00:00 +0000Simulando passagem de tempo em testes de Javascript | Guilherme Garnierhttps://blog.guilhermegarnier.com/2015/09/simulando-passagem-de-tempo-em-testes-de-javascript/
https://blog.guilhermegarnier.com/2015/09/simulando-passagem-de-tempo-em-testes-de-javascript/<p>O <a href="http://jasmine.github.io/">Jasmine</a> é uma das ferramentas de teste para Javascript mais usadas atualmente. A sintaxe estilo BDD lembra bastante o <a href="http://rspec.info/">RSpec</a>, o que facilita a vida de quem já tem experiência com este.</p>
<p>Uma das dificuldades ao realizar testes de Javascript é como simular a passagem do tempo. Existem duas situações básicas onde isso acontece:</p>
<ol>
<li>Quando o código executa alguma animação, como fade in e slide down, por exemplo</li>
<li>Quando definimos uma função que só será executada após um período de tempo determinado</li>
</ol>
<p>O primeiro caso pode ser ilustrado com este exemplo básico:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"button"</span><span class="nt">&gt;</span>Show Menu<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"menu"</span> <span class="na">style=</span><span class="s">"display: none"</span><span class="nt">&gt;</span>Menu<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;script&gt;</span>
<span class="kd">function</span> <span class="nx">example</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#button"</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#menu"</span><span class="p">).</span><span class="nx">fadeIn</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">example</span><span class="p">();</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>
<p>Um clique no botão faz com que o menu apareça usando a função <a href="http://api.jquery.com/fadein/">jQuery.fadeIn</a>. O teste para este código, a princípio, poderia ser algo assim:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">describe</span><span class="p">(</span><span class="s2">"example test"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">beforeEach</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">example</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">it</span><span class="p">(</span><span class="s2">"shows the menu after clicking the button"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#button"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s2">"#menu"</span><span class="p">)).</span><span class="nx">toBeVisible</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
<p>O problema é que, como a animação do fade in leva um pequeno período de tempo para executar (400 ms por padrão), o menu ainda não está visível no momento em que a expectativa é executada. Uma solução inocente, mas pouco eficiente, para este problema seria executar a expectativa num <a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout">setTimeout</a>.</p>
<p>Neste caso específico, como a animação é feita usando jQuery, há uma propriedade <a href="https://api.jquery.com/jquery.fx.off/">jQuery.fx.off</a> que permite desabilitar todas as animações. Desta forma, todas as transições são feitas instantaneamente, fazendo com que o teste original funcione:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">describe</span><span class="p">(</span><span class="s2">"example test"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">jQueryFxOff</span><span class="p">;</span>
<span class="nx">beforeEach</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">jQueryFxOff</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">fx</span><span class="p">.</span><span class="nx">off</span><span class="p">;</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">fx</span><span class="p">.</span><span class="nx">off</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">example</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">afterEach</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">fx</span><span class="p">.</span><span class="nx">off</span> <span class="o">=</span> <span class="nx">jQueryFxOff</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">it</span><span class="p">(</span><span class="s2">"shows the menu after clicking the button"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#button"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s2">"#menu"</span><span class="p">)).</span><span class="nx">toBeVisible</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
<p>Note que o valor original da propriedade é armazenado numa variável e restaurado após o teste, para evitarmos que esta configuração afete outros testes que serão executados em sequencia.</p>
<p>O segundo caso é quando temos algum código que só é executado após um período de tempo - usando <a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout">setTimeout</a>, por exemplo:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">module</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">someRandomCode</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="p">},</span>
<span class="na">waitForIt</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">setTimeout</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">someRandomCode</span><span class="p">,</span> <span class="mi">5000</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">waitForIt</span><span class="p">();</span>
</code></pre></div></div>
<p>A melhor forma de testar este código é “fakeando” a passagem do tempo, para que o teste não precise aguardar. Uma boa ferramenta para isto são os os <a href="http://sinonjs.org/docs/#clock">fake timers</a> do <a href="http://sinonjs.org/">Sinon.JS</a>:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">describe</span><span class="p">(</span><span class="s2">"my random test"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">clock</span><span class="p">;</span>
<span class="nx">beforeEach</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">clock</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nx">useFakeTimers</span><span class="p">();</span>
<span class="nx">spyOn</span><span class="p">(</span><span class="nx">module</span><span class="p">,</span> <span class="s2">"someRandomCode"</span><span class="p">);</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">waitForIt</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">afterEach</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">clock</span><span class="p">.</span><span class="nx">restore</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">it</span><span class="p">(</span><span class="s2">"tests my random code"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">clock</span><span class="p">.</span><span class="nx">tick</span><span class="p">(</span><span class="mi">5000</span><span class="p">);</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">module</span><span class="p">.</span><span class="nx">someRandomCode</span><span class="p">).</span><span class="nx">toHaveBeenCalled</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
<p>Outra boa opção é usar o <a href="http://jasmine.github.io/2.3/introduction.html#section-Jasmine_Clock">Jasmine Clock</a>:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">describe</span><span class="p">(</span><span class="s2">"my random test"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">beforeEach</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">jasmine</span><span class="p">.</span><span class="nx">clock</span><span class="p">().</span><span class="nx">install</span><span class="p">();</span>
<span class="nx">spyOn</span><span class="p">(</span><span class="nx">module</span><span class="p">,</span> <span class="s2">"someRandomCode"</span><span class="p">);</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">waitForIt</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">afterEach</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">jasmine</span><span class="p">.</span><span class="nx">clock</span><span class="p">().</span><span class="nx">uninstall</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">it</span><span class="p">(</span><span class="s2">"tests my random code"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">jasmine</span><span class="p">.</span><span class="nx">clock</span><span class="p">().</span><span class="nx">tick</span><span class="p">(</span><span class="mi">5000</span><span class="p">);</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">module</span><span class="p">.</span><span class="nx">someRandomCode</span><span class="p">).</span><span class="nx">toHaveBeenCalled</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
Thu, 10 Sep 2015 00:00:00 +0000Ferramentas para avaliação de qualidade de código Ruby | Guilherme Garnierhttps://blog.guilhermegarnier.com/2014/11/ferramentas-para-avaliacao-de-qualidade-de-codigo-ruby/
https://blog.guilhermegarnier.com/2014/11/ferramentas-para-avaliacao-de-qualidade-de-codigo-ruby/<p>Ferramentas para avaliar a qualidade do seu código existem aos montes, para qualquer linguagem de programação. Eu já utilizei algumas para Java, mas nunca tinha testado nenhuma para Ruby, apesar de ser a linguagem que mais uso há alguns anos. Por isso, resolvi testar todas as ferramentas que pude encontrar. Separei a avaliação entre serviços e ferramentas.</p>
<h2 id="serviços">Serviços</h2>
<p>Classifiquei como serviços as ferramentas onde, em vez de instalar e executar localmente, você libera acesso ao seu repositório de código a elas, que coletam métricas a partir do código e geram algum relatório. Os dois serviços que avaliei são gratuitos para projetos open source e pagos para projetos com código fechado.</p>
<h3 id="code-climate"><a href="https://codeclimate.com/">Code Climate</a></h3>
<p>Este é certamente o serviço mais conhecido para avaliação de qualidade de código Ruby. Seu foco principal é gerar métricas baseadas em complexidade de código, mas ele também é capaz de identificar algumas falhas de segurança e cobertura de código dos testes.</p>
<h3 id="coveralls"><a href="https://coveralls.io/">Coveralls</a></h3>
<p>O Coveralls funciona de forma semelhante ao Code Climate, porém tem um foco maior em testes. Ele exibe o histórico de cobertura e a diferença para cada arquivo a cada commit.</p>
<h2 id="ferramentas">Ferramentas</h2>
<p>As ferramentas abaixo são open source e distribuídas através de gems. Para utilizá-las, basta instalar a gem e executar um comando, que analisa o código e gera relatórios ou dados brutos para serem analisados.</p>
<h3 id="brakeman"><a href="http://brakemanscanner.org/">Brakeman</a></h3>
<p>O Brakeman é uma ferramenta focada em localizar potenciais falhas de segurança no seu código. Ele também exibe alertas especificamente relacionados ao Rails, como falhas de segurança que já foram corrigidas numa versão do Rails mais recente do que a que você usa.</p>
<p>Como ele tem foco em segurança, é muito importante manter esta gem sempre atualizada, para que ele possa detectar falhas descobertas mais recentemente.</p>
<h3 id="rubocop"><a href="https://github.com/bbatsov/rubocop">RuboCop</a></h3>
<p>O foco do RuboCop é localizar más práticas de programação no seu código, com base no <a href="https://github.com/bbatsov/ruby-style-guide">Ruby Style Guide</a>. Algumas das regras são: uso de aspas simples ou duplas para definir strings, tamanho máximo da linha, número de linhas em cada método e uso de espaços na definição de hashes.</p>
<p>Todas as regras do RuboCop podem ser configuradas ou desabilitadas. Basta criar um arquivo <code class="highlighter-rouge">.rubocop.yml</code> na raiz do projeto com as configurações desejadas. Ele também possui regras específicas para projetos usando Rails.</p>
<p>Se você usa o editor <a href="https://atom.io/">Atom</a>, também é possível executar o RuboCop automaticamente com o plugin <a href="https://atom.io/packages/atom-lint">atom-lint</a>. Assim, ao salvar um arquivo, o RuboCop é automaticamente executado, e os alertas são exibidos no próprio editor, ao lado de cada linha.</p>
<h3 id="rubycritic"><a href="https://github.com/whitesmith/rubycritic">RubyCritic</a></h3>
<p>O RubyCritic foi criado com o objetivo de se tornar uma alternativa gratuita ao Code Climate. Ele gera um relatório bem semelhante ao deste serviço, reportando os trechos mais complexos do seu código.</p>
<h3 id="metric-fu"><a href="https://github.com/metricfu/metric_fu/">Metric Fu</a></h3>
<p>O Metric Fu é um agregador de ferramentas de análise de código. Ele executa diversas ferramentas e gera uma página com links para os resultados de cada uma. É uma das ferramentas mais completas para análise de código Ruby, e uma das mais antigas.</p>
<h3 id="ruby-lint"><a href="https://github.com/YorickPeterse/ruby-lint">Ruby-lint</a></h3>
<p>O foco do Ruby-lint é localizar erros como variáveis não utilizadas ou não inicializadas, ou número errado de argumentos ao executar um método. O problema é que quando extendemos ou incluimos classes ou módulos definidos fora do projeto (em gems), ele não encontra as dependências e dá erro de constante indefinida. Apesar disso, parece que vem sendo bastante atualizado.</p>
<h3 id="reek"><a href="https://github.com/troessner/reek">Reek</a></h3>
<p>Esta ferramenta é um detector de bad smells no código, como Long Parameter List e Feature Envy. Possui plugins para rodar alguns editores, como <a href="http://www.vim.org/">Vim</a> e <a href="http://macromates.com/">TextMate</a>.</p>
<h3 id="roodi"><a href="https://github.com/roodi/roodi">Roodi</a></h3>
<p>Roodi significa “Ruby Object Oriented Design Inferometer”. Ele executa algumas métricas de complexidade de código, mas é bem básico. A maioria das métricas já são calculadas por outras ferramentas apresentadas aqui.</p>
<h3 id="flog"><a href="http://ruby.sadi.st/Flog.html">Flog</a></h3>
<p>Gera um score baseado na complexidade de código. Bem básico.</p>
<h3 id="rails-best-practices"><a href="https://github.com/railsbp/rails_best_practices">Rails Best Practices</a></h3>
<p>Ferramenta bem útil, gera métricas de qualidade baseadas no <a href="http://rails-bestpractices.com/">Rails Best Practices</a>. Como diz o nome, é específico para projetos Rails. Também é disponibilizado como um <a href="http://railsbp.com/">serviço online</a> gratuito, mas somente para projetos públicos no Github. Para projetos privados, é possível instalar uma instância própria, pois <a href="https://github.com/railsbp/railsbp.com">o servidor é open source</a>.</p>
<h3 id="cane"><a href="https://github.com/square/cane">Cane</a></h3>
<p>Mais um gerador de métricas de qualidade. As métricas são parecidas com as do RuboCop.</p>
<h3 id="pelusa"><a href="https://github.com/codegram/pelusa">Pelusa</a></h3>
<p>Este projeto é semelhante ao Ruby-lint, mas não consegui executar. Ele só funciona com <a href="http://rubini.us/">Rubinius</a>, mas é incompatível com a versão mais recente (apesar de o projeto não informar quais são as versões compatíveis).</p>
<h3 id="flay"><a href="https://github.com/seattlerb/flay">Flay</a></h3>
<p>Esta ferramenta procura similaridades na estrutura do seu código (ex: dois métodos que possuem o código muito semelhante).</p>
<h3 id="saikuro"><a href="https://github.com/metricfu/Saikuro">Saikuro</a></h3>
<p>Gera métricas de complexidade ciclomática.</p>
<h3 id="laser-e-nitpick"><a href="https://github.com/michaeledgar/laser">Laser</a> e <a href="https://github.com/kevinclark/nitpick">Nitpick</a></h3>
<p>Mais duas ferramentas semelhantes ao Ruby-lint. Como estão há anos sem atualizações, nem testei.</p>
<h2 id="resumindo">Resumindo…</h2>
<p>Entre as ferramentas que testei, gostei mais do Brakeman, do RuboCop e do RubyCritic. Acredito que são complementares, e, se usadas em conjunto, ajudam bastante a encontrar falhas de segurança, os pontos mais complexos do seu código e a seguir boas práticas de programação Ruby.</p>
<p>Além destas 3, também gosto do Metric Fu, mas por executar muitas ferramentas, acho que ele gera informação demais. Usando ferramentas e métricas em excesso, geramos tanta informação que acabamos ignorando-as. Por isso, preferi focar nas 3 ferramentas que citei, pois já consigo ter um panorama bem completo do status do meu código com elas.</p>
<h2 id="usando-na-prática">Usando na prática</h2>
<p>Todas as ferramentas que testei são executadas via linha de comando e geram como saída algum tipo de relatório. Apesar de poder executá-las manualmente, na minha opinião, é mais interessante executá-las no servidor de integração contínua (CI). Desta forma, garantimos que essas ferramentas serão executadas com frequencia, e todo o time tem acesso aos relatórios gerados, assim como gráficos de evolução a cada execução. Com isso, podemos analisar se a “saúde” do projeto está melhorando ou piorando, basta acompanhar se o número de warnings de uma determinada ferramenta estão aumentando ou diminuindo.</p>
<p>No caso específico do RuboCop, como ele analisa o uso de boas práticas de programação, acho mais útil executar no editor, pois, ao salvar um arquivo, tenho a resposta imediata, e posso fazer os ajustes no mesmo momento. Mas isso é uma questão de preferência.</p>
<h3 id="executando-no-jenkins">Executando no Jenkins</h3>
<p>Como uso o <a href="http://jenkins-ci.org/">Jenkins</a>, descrevi como configurar as ferramentas acima neste servidor. O processo é bem simples, acredito que não seja difícil reproduzí-lo em outros servidores de integração contínua.</p>
<p>Cada ferramenta pode ser executada por um job à parte ou no mesmo job que roda o build e os testes do projeto. Optei pela 1a opção, por 2 motivos:</p>
<ol>
<li>O tempo de execução de cada ferramenta de análise de código pode ser razoavelmente longo, o que deixaria o job de build muito lento</li>
<li>Caso ocorra algum problema na execução de alguma destas ferramentas, não quero que o job de build do meu projeto apareça quebrado no CI. Se o build e todos os testes foram executados com sucesso, o job que executa o build deve ter sucesso</li>
</ol>
<p>Além disso, optei por instalar as gems diretamente no CI. Desta forma, além de não precisar configurá-las no Gemfile do projeto, garanto que as gems estarão sempre atualizadas, o que é muito importante, principalmente no caso do Brakeman, pois novas falhas de segurança são encontradas diariamente.</p>
<h4 id="criando-um-novo-job">Criando um novo job</h4>
<p>Ao criar um novo job no Jenkins, você pode configurá-lo para executar automaticamente após cada build do projeto ou para executar periodicamente. Apesar de a primeira opção garantir que os relatórios de análise de código estarão atualizados a cada build, a execução é um pouco demorada. Além disso, achei que um relatório por dia seria suficiente para acompanhar o status do projeto. Sendo assim, configurei o job de relatórios para executar diariamente, de segunda a sexta-feira. Para isso, na configuração do job, basta selecionar a opção <code class="highlighter-rouge">Build periodically</code>, e no campo <code class="highlighter-rouge">Schedule</code>, digitar <code class="highlighter-rouge">H 0 * * 1-5</code>, por exemplo (o formato é o mesmo usado no <a href="http://pt.wikipedia.org/wiki/Crontab">crontab</a>). Este valor configura o job para ser executado em qualquer minuto da hora zero, em qualquer dia do mês, todos os meses, de segunda a sexta-feira (dias 1 a 5).</p>
<p>Eu optei por criar um único job para executar todas as ferramentas, pois desta forma, tenho todos os relatórios centralizados num único local. A principal desvantagem é que, desta forma, um erro na execução de uma ferramenta fará o job encerrar com status de erro, e as ferramentas seguintes não serão executadas.</p>
<p>Para configurar cada ferramenta dentro do job, o processo é o mesmo:</p>
<ol>
<li>Selecionar em <code class="highlighter-rouge">Build</code> a opção <code class="highlighter-rouge">Execute shell</code>, com os comandos para instalar e executar a gem</li>
<li>Adicionar uma <code class="highlighter-rouge">Post-build Action</code> para exibir os resultados</li>
</ol>
<p>A configuração de cada ferramenta é a seguinte:</p>
<h4 id="brakeman-1">Brakeman</h4>
<p>Configure a execução da ferramenta digitando os seguintes comandos no campo <code class="highlighter-rouge">Execute shell</code> da configuração do job:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir <span class="nt">-p</span> tmp
gem install brakeman <span class="nt">--no-ri</span> <span class="nt">--no-rdoc</span> <span class="o">&amp;&amp;</span> brakeman <span class="nt">-o</span> tmp/brakeman-output.tabs <span class="nt">--no-progress</span> <span class="nt">--separate-models</span> <span class="nt">--quiet</span>
</code></pre></div></div>
<p>Para visualizar os resultados, instale o <a href="https://wiki.jenkins-ci.org/display/JENKINS/Brakeman+Plugin">Brakeman Plugin</a> no Jenkins e selecione em <code class="highlighter-rouge">Post-build Actions</code> a opção <code class="highlighter-rouge">Publish Brakeman warnings</code>. Em <code class="highlighter-rouge">Brakeman Output File</code>, digite <code class="highlighter-rouge">tmp/brakeman-output.tabs</code>.</p>
<h4 id="rubycritic-1">RubyCritic</h4>
<p>Adicione o seguinte comando no campo <code class="highlighter-rouge">Execute shell</code>:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem install rubycritic <span class="nt">--no-ri</span> <span class="nt">--no-rdoc</span> <span class="o">&amp;&amp;</span> rubycritic app lib
</code></pre></div></div>
<p>Para visualizar os resultados, é necessário o plugin <a href="https://wiki.jenkins-ci.org/display/JENKINS/HTML+Publisher+Plugin">HTML Publisher</a>. Após instalá-lo, selecione em <code class="highlighter-rouge">Post-build Actions</code> a opção <code class="highlighter-rouge">Publish HTML reports</code> e digite os seguintes valores:</p>
<ul>
<li><strong>HTML directory to archive:</strong> <code class="highlighter-rouge">tmp/rubycritic</code></li>
<li><strong>Index page[s]:</strong> <code class="highlighter-rouge">overview.html</code></li>
<li><strong>Report title:</strong> <code class="highlighter-rouge">RubyCritic Reports</code></li>
</ul>
<h4 id="rubocop-1">RuboCop</h4>
<p>Digite os comandos abaixo no campo <code class="highlighter-rouge">Execute shell</code>:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir <span class="nt">-p</span> tmp
gem install rubocop <span class="nt">--no-ri</span> <span class="nt">--no-rdoc</span> <span class="o">&amp;&amp;</span> rubocop <span class="nt">--fail-level</span> E <span class="nt">--rails</span> <span class="nt">--out</span> tmp/rubocop.out app lib spec
</code></pre></div></div>
<p>O plugin para exibir o resultado desta ferramenta é o <a href="https://wiki.jenkins-ci.org/display/JENKINS/Warnings+Plugin">Warnings</a>. Após instalá-lo, é necessário configurar um parser para os warnings do RuboCop. Vá até a configuração do Jenkins (<code class="highlighter-rouge">Manage Jenkins</code> -&gt; <code class="highlighter-rouge">Configure System</code>). Em <code class="highlighter-rouge">Compiler Warnings</code>, adicione um novo parser com os seguintes valores:</p>
<ul>
<li><strong>Name:</strong> <code class="highlighter-rouge">RuboCop</code></li>
<li><strong>Link name:</strong> <code class="highlighter-rouge">RuboCop</code></li>
<li><strong>Trend report name:</strong> <code class="highlighter-rouge">RuboCop Warnings</code></li>
<li><strong>Regular Expression:</strong></li>
</ul>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>^([^:]+):(\d+):\d+: ([^:]): ([^:]+)$
</code></pre></div></div>
<ul>
<li><strong>Mapping Script:</strong></li>
</ul>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import hudson.plugins.warnings.parser.Warning
String fileName = matcher.group(1)
String lineNumber = matcher.group(2)
String category = matcher.group(3)
String message = matcher.group(4)
return new Warning(fileName, Integer.parseInt(lineNumber), "RuboCop Warnings", category, message);
</code></pre></div></div>
<ul>
<li><strong>Example Log Message:</strong></li>
</ul>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attributes/default.rb:21:78: C: Use %r only for regular expressions matching more than 1 '/' character.
</code></pre></div></div>
<p>Após salvar esta configuração, volte até a configuração do job e selecione em <code class="highlighter-rouge">Post-build Actions</code> a opção <code class="highlighter-rouge">Scan for compiler warnings</code>. Em <code class="highlighter-rouge">File pattern</code> digite <code class="highlighter-rouge">tmp/rubocop.out</code>, e no campo <code class="highlighter-rouge">Parser</code>, selecione o parser recém-criado, <code class="highlighter-rouge">RuboCop</code>.</p>
Thu, 06 Nov 2014 00:00:00 +0000Abril Pro Ruby 2014: links para as apresentações | Guilherme Garnierhttps://blog.guilhermegarnier.com/2014/05/abril-pro-ruby-2014-links-para-as-apresentacoes/
https://blog.guilhermegarnier.com/2014/05/abril-pro-ruby-2014-links-para-as-apresentacoes/<p>Na última semana aconteceu o evento <a href="http://abrilproruby.com/">Abril Pro Ruby 2014</a>, em Porto de Galinhas. O evento foi excelente, muito bem organizado e com várias palestras e workshops de excelente nível técnico.</p>
<p>No dia de palestras, fiz uma apresentação sobre <a href="https://ggarnier.github.io/arquitetura-distribuida/">Arquitetura Distribuída</a>, onde mostrei uma pequena parte da arquitetura do projeto <a href="http://globotv.globo.com/">Globotv</a>.</p>
<p>Os vídeos das apresentações foram <a href="https://www.youtube.com/playlist?list=PL7a-mWnTar6v5cDC4MLDZKwD0RuMSDHHP">disponibilizados no Youtube</a>. Seguem abaixo os links para cada apresentação e workshop. Alguns estão faltando, mas assim que conseguir, eu atualizo os links do post.</p>
<p>Apresentações:</p>
<ul>
<li><a href="http://twitter.com/gigglegirl4e">Renée Hendricksen</a> - <a href="https://speakerdeck.com/reneedv/classic-algorithms-in-ruby">Classic Algorithms in Ruby</a> - <a href="https://www.youtube.com/watch?v=eVulPTs8wSA">vídeo</a></li>
<li><a href="http://www.twitter.com/joaomdmoura">João Moura</a> - <a href="https://speakerdeck.com/joaomdmoura/building-products-not-apps">Desenvolvendo Produtos, não Aplicações</a> - <a href="https://www.youtube.com/watch?v=YwoE9mDoIIE">vídeo</a></li>
<li><a href="http://twitter.com/marciotrindade">Marcio Trindade</a> - <a href="http://www.slideshare.net/marciotrindade/ruby-21-33969653">As Novidades do Ruby 2.1</a> - <a href="https://www.youtube.com/watch?v=dVevbSA8PKU">vídeo</a></li>
<li><a href="http://www.twitter.com/moonbeamlabs">Lucas Dohmen</a> - <a href="https://speakerdeck.com/moonglum/domain-driven-design-and-nosql">Domain Driven Design &amp; NoSQL</a> - <a href="https://www.youtube.com/watch?v=XJyApnfYONQ">vídeo</a></li>
<li><a href="http://www.twitter.com/arthurnn">Arthur Neves</a> - <a href="https://speakerdeck.com/arthurnn/abril-pro-ruby">Sharding Shopify</a> - <a href="https://www.youtube.com/watch?v=Toqiyb2k_5E">vídeo</a></li>
<li><a href="http://twitter.com/gpgarnier">Guilherme Garnier</a> - <a href="https://ggarnier.github.io/arquitetura-distribuida/">Arquitetura Distribuída: Escalando Projetos e Times</a> - <a href="https://www.youtube.com/watch?v=_0esATer3ss">vídeo</a></li>
<li><a href="http://www.twitter.com/brianxq3">Brian Morton</a> - <a href="https://speakerdeck.com/bmorton/dropping-rails-for-dropwizard-from-abril-pro-ruby-2014">Dropping Rails for Dropwizard?</a> - <a href="https://www.youtube.com/watch?v=Pd3OxF4Ik1o">vídeo</a></li>
<li><a href="http://www.twitter.com/poteland">Pablo Astigarraga</a> - <a href="https://speakerdeck.com/pote/the-dilemma-of-simplicity">The Dilemma of Simplicity</a> - <a href="https://gist.github.com/pote/061f709186c0e93c3f1c">links relacionados</a> - <a href="https://www.youtube.com/watch?v=MOjeNcN1__c">vídeo</a></li>
<li><a href="http://www.twitter.com/rafaelfranca">Rafael França</a> - <a href="https://speakerdeck.com/rafaelfranca/rails-the-hidden-parts">Rails: The Hidden Parts</a> - <a href="https://www.youtube.com/watch?v=nT9xMoNt9S0">vídeo</a></li>
<li><a href="http://www.twitter.com/tjschuck">T.J. Schuck</a> - <a href="https://speakerdeck.com/tjschuck/80-000-plaintext-passwords-an-open-source-love-story-in-three-acts">80,000 Plaintext Passwords: An Open Source Love Story in Three Acts</a> - <a href="https://www.youtube.com/watch?v=1zNVDgA581w">vídeo</a></li>
<li><a href="http://www.twitter.com/scottmotte">Scott Motte</a> - <a href="https://speakerdeck.com/scottmotte/deprecating-the-password-an-alternative-approach-to-authentication-using-email-only">Deprecating the Password: An Alternative Approach to Authentication using Email Only</a> - <a href="https://www.youtube.com/watch?v=JT8qgZWVaOM">vídeo</a></li>
<li><a href="http://www.twitter.com/bonzoesc">Bryce Kerley</a> - <a href="https://speakerdeck.com/bryce/growing-distributed-systems">Growing Distributed Systems</a> - <a href="https://bitly.com/bundles/bonzoesc/b">links relacionados</a> - <a href="https://www.youtube.com/watch?v=hJLXOaUsCrY">vídeo</a></li>
<li><a href="http://www.twitter.com/soveran">Michel Martens</a> - <a href="http://files.soveran.com/minimalism/">The Ruby Minimalist Counterculture</a> - <a href="https://www.youtube.com/watch?v=p9hB6VnrGM4">vídeo</a></li>
<li><a href="http://www.twitter.com/steveklabnik">Steve Klabnik</a> - <a href="https://steveklabnik.github.io/browsers_eat_the_world/">Browsers eat the world</a> - <a href="https://www.youtube.com/watch?v=w5V2mlQIi-g">vídeo</a></li>
</ul>
<p>Workshops:</p>
<ul>
<li><a href="http://www.twitter.com/fromagie">Alex Southgate</a> &amp; <a href="http://www.twitter.com/michigangraham">Matt Graham</a> - Advanced GitHub &amp; Git Course</li>
<li><a href="http://www.twitter.com/akshay_karle">Akshay Karle</a> &amp; <a href="https://twitter.com/leandrogualter">Leandro Gualter</a> - <a href="https://github.com/akshaykarle/coding-in-production">Coding in Production</a></li>
<li><a href="http://www.twitter.com/phlipper">Phil Cohen</a> - Cooking at Shopify: Taming a Busy Kitchen with Many Chefs</li>
<li><a href="http://www.twitter.com/ccjr">Cloves Carneiro Jr</a> &amp; <a href="http://www.twitter.com/twitty_tim">Tim Schmelmer</a> - <a href="http://www.slideshare.net/TimSchmelmer/abril-pro-ruby-2014-workshop">Adopting SOA from Day 1</a> - <a href="https://gist.github.com/timbogit/11346571">material</a></li>
</ul>
Fri, 02 May 2014 00:00:00 +0000Variáveis de classe e de instância de classe em Ruby | Guilherme Garnierhttps://blog.guilhermegarnier.com/2014/02/variaveis-de-classe-e-de-instancia-de-classe-em-ruby/
https://blog.guilhermegarnier.com/2014/02/variaveis-de-classe-e-de-instancia-de-classe-em-ruby/<p>Por ser uma linguagem orientada a objetos, Ruby possui variáveis de instância e de classe. As primeiras se referem a cada instância de uma determinada classe e as segundas à própria classe:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Funcionario</span>
<span class="vc">@@dias_de_ferias</span> <span class="o">=</span> <span class="mi">30</span>
<span class="k">def</span> <span class="nf">salario</span><span class="o">=</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span>
<span class="vi">@salario</span> <span class="o">=</span> <span class="n">valor</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">salario</span>
<span class="vi">@salario</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Como esperado, cada instância da classe <code class="highlighter-rouge">Funcionario</code> possui uma instância da variável <code class="highlighter-rouge">@salario</code>, e a variável <code class="highlighter-rouge">@@dias_de_ferias</code> possui uma única instância:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Funcionario</span><span class="p">.</span><span class="nf">class_variable_get</span><span class="p">(</span><span class="ss">:@@dias_de_ferias</span><span class="p">)</span> <span class="c1"># 30</span>
<span class="n">funcionario1</span> <span class="o">=</span> <span class="no">Funcionario</span><span class="p">.</span><span class="nf">new</span>
<span class="n">funcionario1</span><span class="p">.</span><span class="nf">salario</span> <span class="o">=</span> <span class="mi">2000</span>
<span class="n">funcionario1</span><span class="p">.</span><span class="nf">salario</span> <span class="c1"># 2000</span>
<span class="n">funcionario2</span> <span class="o">=</span> <span class="no">Funcionario</span><span class="p">.</span><span class="nf">new</span>
<span class="n">funcionario2</span><span class="p">.</span><span class="nf">salario</span> <span class="o">=</span> <span class="mi">2500</span>
<span class="n">funcionario2</span><span class="p">.</span><span class="nf">salario</span> <span class="c1"># 2500</span>
</code></pre></div></div>
<p>Até aqui nada de novo. Porém, um tipo de variável menos conhecida e usada é a variável de <em>instância de classe</em>.</p>
<p>Como tudo em Ruby é um objeto, todas as classes (tanto as classes padrão do Ruby quanto as criadas pelo usuário) são objetos - instâncias da classe <code class="highlighter-rouge">Class</code>:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">String</span><span class="p">.</span><span class="nf">class</span> <span class="c1"># Class</span>
<span class="no">Funcionario</span><span class="p">.</span><span class="nf">class</span> <span class="c1"># Funcionario</span>
</code></pre></div></div>
<p>E, como são objetos, as classes também podem ter variáveis de instância. Para definí-las e acessá-las, basta prefixar o nome da variável com “@”, da mesma forma como é feito com variáveis de instância, porém no escopo de classe (ou num método de classe):</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Funcionario</span>
<span class="vi">@bonus</span> <span class="o">=</span> <span class="mi">1000</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">atualiza_bonus</span>
<span class="vi">@bonus</span> <span class="o">=</span> <span class="mi">2000</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>O comportamento de variáveis de instância de classe é semelhante às variáveis de classe, com uma diferença: quando usamos herança, as variáveis de classe são compartilhadas entre todas as classes da hierarquia, e as variáveis de instância de classe tem uma instância para cada classe:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Funcionario</span>
<span class="vc">@@dias_de_ferias</span> <span class="o">=</span> <span class="mi">30</span>
<span class="vi">@bonus</span> <span class="o">=</span> <span class="mi">1000</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Gerente</span> <span class="o">&lt;</span> <span class="no">Funcionario</span>
<span class="vi">@bonus</span> <span class="o">=</span> <span class="mi">5000</span>
<span class="k">end</span>
<span class="no">Funcionario</span><span class="p">.</span><span class="nf">class_variable_get</span><span class="p">(</span><span class="ss">:@@dias_de_ferias</span><span class="p">)</span> <span class="c1"># 30</span>
<span class="no">Funcionario</span><span class="p">.</span><span class="nf">instance_variable_get</span><span class="p">(</span><span class="ss">:@bonus</span><span class="p">)</span> <span class="c1"># 1000</span>
<span class="no">Gerente</span><span class="p">.</span><span class="nf">class_variable_get</span><span class="p">(</span><span class="ss">:@@dias_de_ferias</span><span class="p">)</span> <span class="c1"># 30</span>
<span class="no">Gerente</span><span class="p">.</span><span class="nf">instance_variable_get</span><span class="p">(</span><span class="ss">:@bonus</span><span class="p">)</span> <span class="c1"># 5000</span>
<span class="no">Gerente</span><span class="p">.</span><span class="nf">class_variable_set</span><span class="p">(</span><span class="ss">:@@dias_de_ferias</span><span class="p">,</span> <span class="mi">45</span><span class="p">)</span>
<span class="no">Gerente</span><span class="p">.</span><span class="nf">class_variable_get</span><span class="p">(</span><span class="ss">:@@dias_de_ferias</span><span class="p">)</span> <span class="c1"># 45</span>
<span class="no">Funcionario</span><span class="p">.</span><span class="nf">class_variable_get</span><span class="p">(</span><span class="ss">:@@dias_de_ferias</span><span class="p">)</span> <span class="c1"># 45</span>
</code></pre></div></div>
<p>No exemplo acima, a variável de classe <code class="highlighter-rouge">@@dias_de_ferias</code> é compartilhada entre as classes <code class="highlighter-rouge">Funcionario</code> e <code class="highlighter-rouge">Gerente</code>. Por isso, ao alterar o valor desta variável na subclasse, o valor na superclasse também mudou. Para confirmar que a instância é a mesma, basta verificar que o <code class="highlighter-rouge">object_id</code> da variável em ambas as classes:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Funcionario</span><span class="p">.</span><span class="nf">class_variable_get</span><span class="p">(</span><span class="ss">:@@dias_de_ferias</span><span class="p">).</span><span class="nf">object_id</span> <span class="c1"># 70139308064800</span>
<span class="no">Gerente</span><span class="p">.</span><span class="nf">class_variable_get</span><span class="p">(</span><span class="ss">:@@dias_de_ferias</span><span class="p">).</span><span class="nf">object_id</span> <span class="c1"># 70139308064800</span>
</code></pre></div></div>
<p>No caso da variável de instância de classe <code class="highlighter-rouge">@bonus</code>, há uma instância para a classe <code class="highlighter-rouge">Funcionario</code> e outra para a classe <code class="highlighter-rouge">Gerente</code>:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Funcionario</span><span class="p">.</span><span class="nf">instance_variable_get</span><span class="p">(</span><span class="ss">:@bonus</span><span class="p">).</span><span class="nf">object_id</span> <span class="c1"># 70139307998300</span>
<span class="no">Gerente</span><span class="p">.</span><span class="nf">instance_variable_get</span><span class="p">(</span><span class="ss">:@bonus</span><span class="p">).</span><span class="nf">object_id</span> <span class="c1"># 70139308064780</span>
</code></pre></div></div>
Mon, 24 Feb 2014 00:00:00 +0000Minha palestra no FISL 14 - Design Patterns em Ruby | Guilherme Garnierhttps://blog.guilhermegarnier.com/2013/07/minha-palestra-no-fisl-14-design-patterns-em-ruby/
https://blog.guilhermegarnier.com/2013/07/minha-palestra-no-fisl-14-design-patterns-em-ruby/<p>Na última semana aconteceu a última edição do <a href="http://softwarelivre.org/fisl14">FISL</a>, o Fórum Internacional de Software Livre. No segundo dia do evento, fiz uma palestra sobre Design Patterns em Ruby, a qual foi baseada <a href="/2013/04/design-patterns-em-ruby-decorators-presenters-e-exhibits/">neste post</a>. Todas as palestras do evento foram transmitidas ao vivo, e <a href="http://fisl.org.br/14/papers_ng/public/fast_grid?event_id=3">os vídeos já estão disponíveis</a>.</p>
<p>Disponibilizei o vídeo da minha apresentação <a href="http://vimeo.com/69973911">no Vimeo</a> e os slides <a href="http://www.slideshare.net/GuilhermeGarnier/design-patterns-em-ruby-23952518">no Slideshare</a>.</p>
<iframe src="//player.vimeo.com/video/69973911" width="769" height="460" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
<iframe src="http://www.slideshare.net/slideshow/embed_code/23952518" width="597" height="486" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px" allowfullscreen=""> </iframe>
<div style="margin-bottom:5px"> <strong> <a href="https://www.slideshare.net/GuilhermeGarnier/design-patterns-em-ruby-23952518" title="Design Patterns em Ruby" target="_blank">Design Patterns em Ruby</a> </strong> from <strong><a href="http://www.slideshare.net/GuilhermeGarnier" target="_blank">Guilherme Garnier</a></strong> </div>
<p><strong>UPDATE:</strong> atualizei os slides após apresentar esta mesma palestra no evento <a href="http://rsonrails.com.br">RS on Rails</a>.</p>
Tue, 09 Jul 2013 00:00:00 +0000