<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fred Heath</title>
    <description>The latest articles on DEV Community by Fred Heath (@redfred7).</description>
    <link>https://dev.to/redfred7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F25920%2Fc241623e-4b5f-4292-ad1d-20fb875230a7.png</url>
      <title>DEV Community: Fred Heath</title>
      <link>https://dev.to/redfred7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/redfred7"/>
    <language>en</language>
    <item>
      <title>Agile Myths Exploded</title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Fri, 18 Sep 2020 09:51:06 +0000</pubDate>
      <link>https://dev.to/redfred7/agile-myths-exploded-4lep</link>
      <guid>https://dev.to/redfred7/agile-myths-exploded-4lep</guid>
      <description>&lt;p&gt;One of the few good things about the current pandemic is that it forced people to rethink some of the most rigid 'agile' practices, such as:&lt;/p&gt;

&lt;h3&gt;
  
  
  Co-location
&lt;/h3&gt;

&lt;p&gt;It turns out that development teams can function perfectly well in an agile environment without being physically located in the same space. We kind of suspected that due to the pre-pandemic success of many remote-working companies, such as Basecamp, Zapier, Moleskine and many others. But now most of us found out that -with a few workflow adjustments- any team can be productive when working remotely. Not only that, but the lack of commuting and workplace distractions means that many people can be more productive away from the office rather than in it. It also means that we can include people who would find it hard to commute to an office every day, such as disabled ppl or ppl with parenting/caring commitments. &lt;/p&gt;

&lt;h3&gt;
  
  
  Stand-ups
&lt;/h3&gt;

&lt;p&gt;These were short meetings where attendants would be literally standing up. The idea was that this would encourage short and focused meetings. The end of co-location also meant the end of stand-ups. People discovered that they could have short, focused meetings on Zoom or Skype. Turns out, the duration and focus of meetings depends on the attitude and culture of the participants and organizers, not on everyone standing up together in a room. Who would have thought? &lt;/p&gt;

&lt;h3&gt;
  
  
  "No documentation needed", a.k.a "You just need to have a conversation"
&lt;/h3&gt;

&lt;p&gt;This is one of &lt;em&gt;the most abused notions in agile&lt;/em&gt;. One of the Agile principles is that &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The most efficient and effective method of conveying information to and within a development team is face-to-face conversation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is absolutely true. Sure, you may need to repeat the same thing to different people many times.  And, yes, some information gets lost through verbal transmission, as the 'telephone / chinese whispers' game demonstrates. Also, some ppl are much more receptive to visual info, rather than verbal. &lt;/p&gt;

&lt;p&gt;But, overall, talking to ppl is the best way to convey information.  However, it should never be &lt;em&gt;the only way&lt;/em&gt;. The Agile Manifesto doesn't state that we shouldn't be documenting things. Just that we prefer working software over comprehensive documentation. &lt;/p&gt;

&lt;p&gt;You have to realise that at the time the manifesto was written, working code was the last thing a software team was meant to produce. First it needed to deliver documentation: SDDs, STDs, SRSs and other horrible acronyms, were some of the bureaucratic documents we had to deliver before even stating to think about coding (I know, I used to do this, I'm that old!). An analyst would write a requirements document and then hand it off to the development team and forget about it. The agile founders said: "This is stupid and counter-productive. Stop it!". For that, I am eternally grateful. But they never said to not document what we do.&lt;/p&gt;

&lt;p&gt;The pandemic exposed the weaknesses on relying on conversations and omitting documentation. Before, if we needed some information we could just turn round and ask the person at the next desk. It was easy to just shout out something across the office so that everyone would hear. It is no longer that easy.  The immediacy of response is lost. Now, putting a message on the team slack channel does not mean you'll get back an immediate response or even acknowledgement. Also, talking about something is much quicker than writing. If I have to write a response to the same question again and again, I may as well paste that response in a document and link to it.&lt;/p&gt;

&lt;p&gt;People have to rely on documents and diagrams much more now, to ensure that information is conveyed reliably and accurately. This is how it should be. Documentation is persistent in way that verbal communication can never be. It is more resistant to corruption. It also helps clarify or enlighten in ways that verbal communication can't (try explaining complex/parallel workflows without sequence diagrams, for instance).&lt;/p&gt;

&lt;p&gt;Conversations should form the core of information flow, but they must always be supplemented by proper documentation.   &lt;/p&gt;

&lt;h3&gt;
  
  
  "UML is not agile/too elaborate/old-fashioned/...etc"
&lt;/h3&gt;

&lt;p&gt;A side-effect of the "no documentation" fallacy was this myth. UML was a 'waterfall' thing. Only old ppl use UML.  It's not needed in agile world. &lt;/p&gt;

&lt;p&gt;The thing is, UML is just a notation. Yes, it's been used as the basis of rigid and complex methodologies. But that's not UML's fault. One of the pandemic consequences of ppl starting to use more diagrams in order to convey information (see previous section), was that the lack of common standards became exposed.  What does that dotted line in your diagram mean? How is that rectangle different to that oval shape? Does that thing represent a class or a database table? If only we had a universal notation so that we can all express our designs in the same way.  If only we had a ubiquitous visual 'language' understood by all.&lt;/p&gt;

&lt;p&gt;....hold on. We do! It's called &lt;a href="https://www.tutorialspoint.com/uml/index.htm"&gt;UML&lt;/a&gt;. It helps us communicate our thoughts visually and uniformly.Good communication is critical to an agile workflow, so UML is too, as many more people are beginning to realise. &lt;/p&gt;

&lt;h3&gt;
  
  
  Epilogue
&lt;/h3&gt;

&lt;p&gt;Often, certain practices may not make sense to us but we are reluctant to call them out because of peer pressure and cultural acquisition. Sometimes it takes a universal and external event to expose the weaknesses or irrelevance of these practices. The pandemic is making us rethink much of the way we work and live our lives. But despite the many negative effects, it can also help us change some things for the better. Stay safe and keep productive everyone! &lt;/p&gt;

</description>
      <category>agile</category>
      <category>covid</category>
      <category>remote</category>
      <category>uml</category>
    </item>
    <item>
      <title>Managing Software Requirements the Agile Way</title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Wed, 19 Aug 2020 10:39:51 +0000</pubDate>
      <link>https://dev.to/redfred7/managing-software-requirements-the-agile-way-j7j</link>
      <guid>https://dev.to/redfred7/managing-software-requirements-the-agile-way-j7j</guid>
      <description>&lt;p&gt;My book, titled as above, has now been published. One of the reasons I'm writing about it here was that the dev.to community played a part in my book becoming a reality. Allow me to explain the how and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;I've been working in software engineering for well over two decades. In the last 10 years or so, I've been working with agile processes and frameworks, such as Scrum, Scrumban and others. One of the things that always struck me with agile implementations was how lackadaisical their approach to capturing and managing requirements was. I went from a bureaucratic, documentation-laden, analysis-paralysis, waterfall cycle to a "let's write a user-story and have a chat about it", 'agile' approach. At first this seemed like a really cool, laid-back way of doing things. Over the years though, I started to realise how inefficient and ineffective this approach was. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TLSDD1d6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/20f645twt2wajinarvd8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TLSDD1d6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/20f645twt2wajinarvd8.jpg" alt="user story hell" title="From user story hell..."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;User Stories as the foundation of a development and delivery cycle are dubious, for reasons too numerous to mention here (I describe them in the book). One of their many problems is that they don't help provide a &lt;em&gt;Specification&lt;/em&gt;, that is a way of knowing how our software will realise the users' requirements. At best, a user story will have some acceptance criteria that the developers will use to verify their code output. However, &lt;em&gt;the output is not the outcome&lt;/em&gt;.  Just because our code produces an expected result does not mean we satisfied the user's need, nor that we helped them realise their goal. To achieve that we need to work the other way round: identify the stakeholders' goals, then the impact the stakeholders need to have on our system in pursuit of their goals and finally the system functionality which will realise that impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  The light at the end of the tunnel (no it wasn't a train)
&lt;/h2&gt;

&lt;p&gt;I found &lt;a href="https://cucumber.io/docs/bdd/"&gt;Behavior Driven Development (BDD)&lt;/a&gt; to be a great way to create specifications that can also be verified against the deployed software. BDD is centred around Features, so the big question became "How do I reliably create or derive valid Features based on the requirements?" This puzzled me for a long time until I came across &lt;a href="https://www.impactmapping.org/"&gt;Impact Mapping&lt;/a&gt;. Impact Mapping allowed me to answer the big question of What, Why, How and for Whom are we doing what we're doing (spoiler: the What part is our Features). It also provided a great way to bridge the gap between Requirements and Specifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S9WKkwAD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1rsc6hglyzliyrgvmxmf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S9WKkwAD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1rsc6hglyzliyrgvmxmf.png" alt="a Requirements Model" title="...to a structured and traceable Requirements Model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After years of trial and error, I finally managed to refine a working methodology for capturing and modelling requirements, creating executable specifications for these requirements and delivering them within an agile process or method like Scrum or Kanban. So for the last couple of years I'd been thinking of putting all this knowledge down in a book but I never felt bold enough to go for it. Until last year.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Post
&lt;/h2&gt;

&lt;p&gt;In April 2019 I posted my thoughts on the user-story chaos in an article titled &lt;a href="https://dev.to/redfred7/enough-with-the-user-stories-already-2a8a"&gt;Enough with the User Stories already!&lt;/a&gt; It became my most read post here on dev.to. It generated many comments and I also received a few emails creating conversation on some of the points in the article.  It triggered my decision to write what I know and practice in a book, and set it free out in the wild.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Book
&lt;/h2&gt;

&lt;p&gt;So here comes the self-advertising part :) My book contains an end-to-end methodology for building the right software. It shows techniques, methods and tips for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eliciting requirements from your Stakeholders&lt;/li&gt;
&lt;li&gt;Modelling requirements with Impact Mapping&lt;/li&gt;
&lt;li&gt;Defining behaviour in Features&lt;/li&gt;
&lt;li&gt;Writing verification code for your Features&lt;/li&gt;
&lt;li&gt;Creating a feature-based product backlog&lt;/li&gt;
&lt;li&gt;Deliver the right software within Scrum or Kanban&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Along some established techniques like BDD and Impact Mapping, I also demonstrate some home-grown ones like parsing requirements with D3 and software delivery with Feature-first development. The book won't show you how to create software correctly, but I'm confident it will help you create and deliver the correct software ;)&lt;/p&gt;

&lt;p&gt;The book is out on &lt;a href="https://www.packtpub.com/business-other/managing-software-requirements-the-agile-way"&gt;Packt Pub&lt;/a&gt; and &lt;a href="https://www.amazon.co.uk/gp/product/B08BWR47L9/ref=dbs_a_def_rwt_bibl_vppi_i0"&gt;Amazon&lt;/a&gt;.  Hope you find it useful and thanks to everyone who's contributed to the user-story and requirements discussion.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>agile</category>
      <category>behaviordrivendevelopment</category>
      <category>impactmapping</category>
    </item>
    <item>
      <title>Career advice for junior developers</title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Tue, 14 Apr 2020 10:37:40 +0000</pubDate>
      <link>https://dev.to/redfred7/career-advice-for-junior-developers-4kmg</link>
      <guid>https://dev.to/redfred7/career-advice-for-junior-developers-4kmg</guid>
      <description>&lt;p&gt;I had a nice chat this morning with our new intern, who's due to start working with us this summer. One of the things he asked me was if I could give him any tips about things he should be looking to learn in order to advance his career in programming. In my mind, I started formulating a list of programming languages, web frameworks, libraries and technologies. I was then struck by the thought that most things on my list would be obsolete in 5-10 years. I considered whether knowing the intricacies of a specific language, or how a framework is put together or how the Model-View-Controller paradigm works, was a solid foundation for a successful career in Software Development. The clear answer to my own question was that no, all these things are transient. They are here today, gone tomorrow. &lt;br&gt;
An old proverb sprang to mind: "You give someone a fish, they'll eat for a day. Teach them to fish and they'll be eating for a lifetime".&lt;/p&gt;

&lt;p&gt;So this is what I told him: "Learn whatever catches your eye, excites you and you feel comfortable with. But whatever you do, keep in mind the following":  &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Enjoy what you do
&lt;/h3&gt;

&lt;p&gt;You'll be spending one of the biggest parts of your life working (the other big part is sleeping but we can't help that). If the tools or people you're working with frustrate, impede or depress you, then change them. Sometimes, this may require changing jobs. That's fine, you have no obligation to suffer in exchange for money or out of some mis-placed sense of loyalty. By enjoying what you do at work, you're going to be better at it and more productive. Enjoying being round your colleagues will make you more collaborative and help you learn new things. Most importantly, by working in an enjoyable environment you will also be safeguarding your mental health, this most precious of goods.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Coding isn't enough
&lt;/h3&gt;

&lt;p&gt;To be a good developer you need to be able to code. To be a great developer you need to be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Communicate your assumptions and expectations to others.&lt;/li&gt;
&lt;li&gt;Empathise with others and understand their point of view.&lt;/li&gt;
&lt;li&gt;Plan ahead with forethought and discretion.&lt;/li&gt;
&lt;li&gt;Manage other people's expectations of you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unfortunately, there aren't any boot camps for these skills. These are skills you need to hone and cultivate yourself. They take time and effort and not everyone manages to accomplish all of them. But mastering these skills can make all the difference between yet another coder and an effective software developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. You read more code than you write
&lt;/h3&gt;

&lt;p&gt;You may not think so, but, trust me, you do. So what does this mean? It means that when you write code you must keep in mind that someone else (or even worse, you 12 months later) will have to read it. So make sure your code is readable, well-documented and trimmed down to what is needed to deliver the intended piece of functionality. Ensure your codebase is &lt;em&gt;not&lt;/em&gt; bloated with clever code hacks or complex design patterns and architectures which only exist to inflate people's egos or adhere to some dogmatic pattern or methodology. Keep your code simple, modular and readable. Your colleagues and future self will thank you for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Separate the things that change from the things that stay the same
&lt;/h3&gt;

&lt;p&gt;Everything changes over time, but there are things that change at predictable occasions or at regular frequency. Make sure these things are separated from stuff that doesn't often change. So keep your UI code well away from your business-logic code. Keep your data-reading code away from your data-formatting code. If you are dealing with external APIs, hide them behind an adaptor or facade. The APIs will be changing a lot more frequently than your internal usage of them, i.e. your facade. Apply this principle throughout your code and designs. &lt;/p&gt;

&lt;h3&gt;
  
  
  5. Develop iteratively and incrementally
&lt;/h3&gt;

&lt;p&gt;Never try to build a complete and perfect system in one go. Focus on the most needed and clear features first and provide the simplest working solution to them. Then build-up your project over many iterations, each iteration refining existing features and delivering new ones. &lt;/p&gt;

&lt;p&gt;If you're not sure what an iterative and incremental approach is, then &lt;a href="https://itsadeliverything.com/revisiting-the-iterative-incremental-mona-lisa"&gt;this&lt;/a&gt; should help clarify it.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Premature optimisation kills projects
&lt;/h3&gt;

&lt;p&gt;Do not try to anticipate future needs without solid evidence that these needs are likely to arise. If your web-site ever needs to handle 10 zillion requests per minute then don't worry, at that stage you'll be making enough money to comfortably retire at your own private island, so focus on that instead ;) .The point is, do not optimise pre-maturely, 'just in case'. Do not work on the basis of 'what-ifs'. If you do, you'll only add unnecessary complexity in your code, increase the likelihood of bugs and reduce its readability. On that note, prefer languages and frameworks that promote flexibility and ease-of-change.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. No Silver Bullet
&lt;/h3&gt;

&lt;p&gt;To quote &lt;a href=""&gt;Mr Brooks&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is no single development, in either technology or management technique, which by itself promises even one order of magnitude [tenfold] improvement within a decade in productivity, in reliability, in simplicity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Over your career you'll see many 'hotnesses' come and go. You'll hear people raving on about the fastest web framework, the coolest design paradigm, the best programming language, and so on. All these things may be true within the narrow slice of reality of the people clamouring for them, but they are not necessarily true in your reality. Complex problems, like the ones we face in software development, are never solved by simple solutions, like using a single programming language or web framework. Never be fooled by thinking you found that one thing which will solve all your programming problems. &lt;/p&gt;

&lt;h3&gt;
  
  
  8. Generalise, don't specialise
&lt;/h3&gt;

&lt;p&gt;In nature, the species of animals more likely to survive are the generalist species, that is the species which can exploit different environments and food sources. In contrast, the species more likely to go extinct are the specialists, the ones that rely on a single food source or can only thrive in one environment. The same applies in software development. Do not put all your eggs in one basket, as the saying goes. The ability to do many things well, will see you farther than the ability to one thing brilliantly. &lt;/p&gt;

&lt;h2&gt;
  
  
  Epilogue
&lt;/h2&gt;

&lt;p&gt;Here you go, I'm sure there are a couple of more items I forgot to add, but I think these capture the fundamental principles I've learned over many years as a software developer. I wish someone told me these things when I was first starting out. Hopefully they may help someone else, so thanks for reading and feel free to add your own advice in the comments.  &lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>beginners</category>
      <category>career</category>
      <category>programming</category>
    </item>
    <item>
      <title>Smart, nice and gets things done. What to look for when hiring devs! </title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Tue, 08 Oct 2019 21:48:07 +0000</pubDate>
      <link>https://dev.to/redfred7/smart-nice-and-gets-things-done-what-to-look-for-when-hiring-devs-3m52</link>
      <guid>https://dev.to/redfred7/smart-nice-and-gets-things-done-what-to-look-for-when-hiring-devs-3m52</guid>
      <description>&lt;p&gt;Joel Spolsky famously coined the phrase &lt;a href="https://www.joelonsoftware.com/2007/06/05/smart-and-gets-things-done/"&gt;Smart and Gets Things Done&lt;/a&gt; back in the naughties, when summarising the qualities he was looking for when recruiting new hires. This phrase quickly became the mantra of many a start-up and recruitment firm as it seemed to fully describe the kind of people one would want to work with.&lt;/p&gt;

&lt;p&gt;Smart, or 'clever' in UK English, is a much desirable trait. I mean who doesn't want to work with clever people, right? People who can grasp difficult concepts quickly and easily, people who can find innovative solutions to difficult problems, optimise an existing design to the max, see challenges where others see dead-ends, etc. Having smart people in your team gives you a great competitive advantage for sure. &lt;/p&gt;

&lt;p&gt;People who 'get things done' is another favoured trait. Efficiency is highly-valued in our industry. I mean, you can employ the smartest, brainiest, genius in the world but if they can't (or aren't willing) to get their hands dirty and implement the solution they dreamed up, or deploy their code to CI, or document their design, then they aren't much use are they? &lt;/p&gt;

&lt;p&gt;So yes, people who are smart and get things done is an ideal combination.   Or is it.....?&lt;/p&gt;

&lt;p&gt;Many years of working in software teams have taught me that there is another trait that we should value as much as any other: Niceness.  Now, niceness is a very broad term and can be used to describe a variety of characteristics. So let me narrow it down. A nice person is someone who has the following,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Empathy&lt;/li&gt;
&lt;li&gt;Courtesy&lt;/li&gt;
&lt;li&gt;Honesty&lt;/li&gt;
&lt;li&gt;Generosity&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Allow me to elaborate:&lt;/p&gt;

&lt;h3&gt;
  
  
  Empathy
&lt;/h3&gt;

&lt;p&gt;Is the ability to understand and be aware of the feelings and experiences of another person. An empathetic person is someone who offers to help you with your workload when you are struggling, instead of shrugging and saying "it's not my problem". An empathetic boss is someone who -when they see you down with a bad cold, or plagued by a family or personal problem- say "Take a couple of days off to recover" instead of "Are you going to meet the deadline on Friday?". Lack of empathy is a strong indicator of sociopathic behaviour or psychopathy. Empathetic people, on the other hand, tend to make a team function more productively and much less stressfully. &lt;/p&gt;

&lt;h3&gt;
  
  
  Courtesy
&lt;/h3&gt;

&lt;p&gt;A fundamental level of politeness to be extended to other people. You don't have to know someone in order to be courteous to them. Heck, you don't even have to like them. Being polite to someone not only makes them feel better, it also makes you more likely to be approached by others, invited to their social gatherings and -very importantly in a work environment- to get listened to.   &lt;/p&gt;

&lt;p&gt;Don't get me wrong, I would never advocate being courteous to someone who harasses or bullies you, such people take courtesy as a weakness. But, under normal circumstances, being polite and pleasant even to the most annoying colleagues will make you both feel like better persons.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Honesty
&lt;/h3&gt;

&lt;p&gt;Telling the truth is not always a black and white process. It usually works best combined with Empathy. Would you tell a colleague that they have spinach stuck to their teeth? Maybe not, as it would embarrass them. But what if they were about to walk into a very important meeting? It's better that they embarrass themselves to you than in front of the executive board or a very important client. Weighing the consequence of telling the truth is not easy but is something we always need to do instead of just choosing the easy way. The easy way, for instance, would be not to tell your team that the solution you developed fails under some extreme scenario. After all, it works 99% of the time so who needs to know about the 1%, right? Well, they don't until one of your colleagues does an important demo for a client and they hit that 1% scenario and the system crashes and burns. Being honest may be painful in the short term but always pays in the long term. &lt;/p&gt;

&lt;h3&gt;
  
  
  Generosity
&lt;/h3&gt;

&lt;p&gt;Generosity isn't just about material giving. Yeah, we all love the person who comes in the office with a big bag of doughnuts every week or who buys coffee for everyone when going to the cafe next door. But it's generosity of spirit that makes the difference. A person of generous spirit takes complete responsibility for their lives and work. They do not blame others or circumstances for their problems.  They own their mistakes and admit them.  They are not afraid to give their time in order to help someone else. They don't boast about their victories and they are happy to share their successes with others.&lt;/p&gt;

&lt;p&gt;I've worked in software in many different areas, in various roles and with a variety of people. I've learned to value the above qualities in my colleagues as much -or even more- than I value their intelligence and effectiveness. Our industry has devised many ways to detect and measure people who are 'smart' and 'get things done' but sadly no way to find people whose character qualities can improve a team so very much. So yeah, go ahead and hire smart people who get things done but also make sure they are &lt;strong&gt;nice&lt;/strong&gt; people. You won't regret it!   &lt;/p&gt;

</description>
      <category>culture</category>
      <category>career</category>
      <category>hiring</category>
    </item>
    <item>
      <title>So, do you know how to build a software system for your client? (part 3 - modelling Requirements with Impact Maps)</title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Mon, 23 Sep 2019 08:33:36 +0000</pubDate>
      <link>https://dev.to/redfred7/so-do-you-know-how-to-build-a-software-system-for-your-client-part-3-modelling-requirements-with-impact-maps-h19</link>
      <guid>https://dev.to/redfred7/so-do-you-know-how-to-build-a-software-system-for-your-client-part-3-modelling-requirements-with-impact-maps-h19</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/redfred7/so-do-you-know-how-to-build-a-software-system-for-your-client-part-4-modelling-requirements-with-impact-maps-1k94-temp-slug-2943160?preview=3cf48781863e862d427a178df59fb768736fe3a898086ffaab28de487bb266a045d62009cc9e16b0943e5cfdab7ba6f55c5409359ddc6942153f07ec"&gt;the previous article&lt;/a&gt; of this series we explored the Requirements domain entities, i.e. the things we need to know about if we want to capture and manage our Requirements correctly and efficiently. These entities were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stakeholders and Actors&lt;/li&gt;
&lt;li&gt;Business Goals&lt;/li&gt;
&lt;li&gt;Capabilities&lt;/li&gt;
&lt;li&gt;Features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we'll learn how these entities are related and how we can model them in a visual and intuitive manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impact Mapping
&lt;/h2&gt;

&lt;p&gt;Back in 2012, Gojko Adjiz defined the concept of Impact Maps, a technique that he evolved from UX-based effect-mapping methods, to improve communication, collaboration and interaction in teams and organisations. Impact Mapping allows us to explore, analyse the discover answers to the major questions we have when starting to build a system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why are we doing it?&lt;/li&gt;
&lt;li&gt;Who will benefit from it?&lt;/li&gt;
&lt;li&gt;What do we need to do?&lt;/li&gt;
&lt;li&gt;How are we going to do it? &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answer to these questions help us discover the entities involved in our Requirements and Specification model&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business Goals (Why)&lt;/li&gt;
&lt;li&gt;Stakeholders (Who)&lt;/li&gt;
&lt;li&gt;Capabilities (What)&lt;/li&gt;
&lt;li&gt;Features (How)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can additionally discover and define Tasks, that is the units of work we need to do in order to build our system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F306kpq7y2ggnhmam30vx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F306kpq7y2ggnhmam30vx.png" alt="Example Impact Map"&gt;&lt;/a&gt;&lt;em&gt;Example Impact Map&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, how do we get to create a well-defined Impact Map armed with only our client's wishes and desires (i.e. their raw requirements)? Read on to find out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The D3 method
&lt;/h2&gt;

&lt;p&gt;As System Builders we have requirements thrown at us from many directions: client representatives, project managers, product owners, business analysts and fellow developers, they all want something from our system. These requirements come in many shapes and forms: formal statements, conversational statements, user stories, flowcharts and other diagrams and many other forms. Our job is to analyse these requirements and model the relevant entities in an Impact Map.  Let's see how we would go about this task:&lt;/p&gt;

&lt;p&gt;Let's say our client has told us they want their system to notify users when a certain product they have bought previously goes on sale. They might have expressed this in a formal statement ("The system shall notify user who have previously bought laptops..."), as a user-story ("As a past buyer of a laptop, I want to be notified...") or simply in an over-the-phone verbal exchange.  To analyse and model this, we shall use a simple method I call D3: Decomposition-Derivation-Discovery.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decomposition
&lt;/h3&gt;

&lt;p&gt;Decomposition is the breaking down of the requirement to determine how many and what kind of entities it represents (Business Goals, Capabilities, Features or Tasks). A good heuristic for doing this is looking at the verbs of the main clause i.e. "The system shall notify..", "I want to be notified...", etc.&lt;/p&gt;

&lt;p&gt;By examining our sample requirement in this light, it becomes apparent that there is only one entity involved and it has to do with notifications. It could have been that there were more than one verbs in the main clause, in which case we'd be dealing with two entities, but luckily that's not the case in our example.&lt;/p&gt;

&lt;p&gt;The next step is to decide which type of entity it is. Obviously the whole 'notifications' thing isn't a Business Goal or a simple Task, so it's either a Feature or a Capability. A Capability is a high-level, coarsely-grained ability the actors can leverage in order to achieve their Goals. It answers the question "What can the system do for the actor?" A Feature is a medium-to-low-level, fine-grained piece of functionality that the system provides to it's actors. It answers the question "How can the actor realise this Capability?"&lt;/p&gt;

&lt;p&gt;Given that what we know about the requirements is an abstract wish to notify users under certain conditions, then it would be reasonable to assume that we are dealing with a system Capability. Let's give it an appropriate name and add it to our model:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flposayhxurtvdp0vx7x4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flposayhxurtvdp0vx7x4.png" alt="Defining a Capability"&gt;&lt;/a&gt;&lt;em&gt;Defining a Capability&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Derivation
&lt;/h3&gt;

&lt;p&gt;Derivation is the creation of other Requirement entities that our original entity depends on, or that are dependent on our entity.  So far we have identified a Notification Capability for our system. As every Capability must exist to realise a Business Goal, we must ask ourselves what the Business Goal is in our case. It's best not to make assumptions at this point and converse with our client in order to understand what they want to achieve by offering this Capability.  Remember that we can and must validate the Business Goal, according to the standards described &lt;a href="https://dev.to/redfred7/so-do-you-know-how-to-build-a-software-system-for-your-client-part-2-439n"&gt;here&lt;/a&gt;. Let's say that in our case our client wants to use this Capability to entice their customers to buy more stuff, thereby increasing the client's revenue. That's a perfectly valid Goal, so let's add it to our model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fck2hn8hs4zenmfjyk12x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fck2hn8hs4zenmfjyk12x.png" alt="Defining a Goal"&gt;&lt;/a&gt;&lt;em&gt;Defining a Goal&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now let's look at the right side of our Impact Map. We haven't yet defined any Features for our Capability. Let's do so now. We simply need to answer the question "How can we Notify users of product sales?", or else "How can we deliver the Notifications Capability?"  Two possible answers would be: 1) Email notifications and 2) In-app notifications.  As usual, we need to check with our client that these Features are acceptable to them. Assuming that they are, let's go ahead and add them to our Impact Map. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fx16n5bsxmbbp2xm1sa9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fx16n5bsxmbbp2xm1sa9y.png" alt="Defining Features"&gt;&lt;/a&gt;&lt;em&gt;Defining Features&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Finally, let's get down to the nitty-gritty of it and create some Tasks for each of our Features. Tasks are pieces of work that need to be done in order to implement a Feature. Most often they will involve coding, but they may also be about writing documentation, setting up a CI/CD pipeline or anything else that needs to get done to get our Feature up and running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6uukrf8eegooqso5y6xi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6uukrf8eegooqso5y6xi.png" alt="Defining Tasks"&gt;&lt;/a&gt;&lt;em&gt;Defining Tasks&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And that concludes the Derivation phase. We started with a single requirement and now we have a fully-populated Impact Map. But we're not done yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discovery
&lt;/h3&gt;

&lt;p&gt;Often the most overlooked phase, Discovery is about discovering new Capabilities that our system may need. The Business Goal is a good starting point. Are there any other Capabilities our system could offer to help attain that Goal?  In our example our client wants to entice users to buy more so we're offering the 'Notification of sales' Capability. Another Capability that would help towards this would perhaps be a 'Discount for bulk buying' Capability. This is something we could (and should) discuss with our client. After all, we are more than just code monkeys, we are System Builders who want to help our clients achieve their Goals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frio4pgco7u87jz2nfa3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frio4pgco7u87jz2nfa3g.png" alt="Discovering other Capabilities"&gt;&lt;/a&gt;&lt;em&gt;Discovering other Capabilities&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Naturally, once we add new system Capabilities we should go back to the Derivation phase and make sure we create some Features and Tasks for our new Capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Epilogue
&lt;/h3&gt;

&lt;p&gt;So there you have it, we started with our client's raw requirements and used a structured approach to create a well-defined model of our Requirements and Specifications. There is one more step we need to go through before we can start coding away and this is to detail our Features. We'll do that by writing some Executable Specifications. More of that in the next article :)&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>agile</category>
      <category>requirements</category>
      <category>specifications</category>
    </item>
    <item>
      <title>So, do you know how to build a software system for your client? (part 2 - the Requirements Domain)</title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Thu, 11 Jul 2019 19:32:15 +0000</pubDate>
      <link>https://dev.to/redfred7/so-do-you-know-how-to-build-a-software-system-for-your-client-part-2-439n</link>
      <guid>https://dev.to/redfred7/so-do-you-know-how-to-build-a-software-system-for-your-client-part-2-439n</guid>
      <description>&lt;h1&gt;
  
  
  The Requirements Domain
&lt;/h1&gt;

&lt;p&gt;In every knowledge domain, be it medicine, manufacturing or software development, we need to have a mental model of the domain entities involved and their interactions, before we can start thinking about providing solutions in that area. A Domain Model is a representation of all the important entities, activities and their relationships in a specific Domain.&lt;/p&gt;

&lt;p&gt;So, for instance, in the Pizza-making Domain (one of my favourites) we would expect to find entities such as Dough, Toppings, Restaurant, Delivery Person, etc. Similarly, in the Requirements Domain we'd expect to find entities and relationships that help us conceptualise requirements. &lt;/p&gt;

&lt;p&gt;The entities of the Requirements Domain are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stakeholders&lt;/li&gt;
&lt;li&gt;Business Goals&lt;/li&gt;
&lt;li&gt;Capabilities&lt;/li&gt;
&lt;li&gt;Features&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stakeholders and Actors
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;Stakeholder&lt;/em&gt; is someone who derives value, benefits from or influences our system. An &lt;em&gt;Actor&lt;/em&gt; is a Stakeholder who interacts with our system, either directly or indirectly. All Actors are Stakeholders, but a Stakeholder is not necessarily an Actor&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dyj9pdrabxlq5jh2j17.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dyj9pdrabxlq5jh2j17.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a small, but important sub-set of Stakeholders which is the set difference between Stakeholders and Actors. We call these 'Non-acting Stakeholders'. These would usually be people like Directors, Business Owners, Enterprise Architects or Senior Managers. They will have a stake in the system and they will likely make decisions that influence the system, but don't expect to see them sitting down in front of a screen and using the system any time soon.&lt;/p&gt;

&lt;p&gt;Actors may be divided into two categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Primary Actors interact with system directly through a UI or API in order to achieve a specific goal.&lt;/li&gt;
&lt;li&gt;Secondary Actors are actors that the system needs assistance from in order to achieve the Primary Actor’s goal.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Business Goals
&lt;/h2&gt;

&lt;p&gt;Goals that benefit the Stakeholders. Business Goals are usually defined by Non-acting Stakeholders, such as Directors, Senior Managers, etc. The Business Goal's incentive and ultimate outcome should ultimately fall under one of these categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increasing revenue&lt;/li&gt;
&lt;li&gt;Reducing costs&lt;/li&gt;
&lt;li&gt;Protecting revenue&lt;/li&gt;
&lt;li&gt;Avoiding future costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful technique for determining a Business Goal's incentive is the 5 Whys technique. If after the 5 questions the answer isn't one of the above, then you should seriously question the value this Goal adds to the Business. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example&lt;/em&gt;: Reward loyal customers&lt;br&gt;
Why? So that returning customers can feel valued and also save some money &lt;br&gt;
Why? So that customers keep returning more often &lt;br&gt;
Why? So that they spend more money on our products (increase revenue)&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Tip:&lt;/b&gt; Good Business Goals add value to Stakeholders by specifying goals tangential or extrinsic to the system's inherent abilities and functionality. For instance,  'I want customers to buy books' is not a valid goal for an online book-store.  'I want customers to buy more books from us than competitor X' is  better, though it still lacks a strategic aspect. Speaking of which...&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategic goals  are the best goals
&lt;/h3&gt;

&lt;p&gt;The best Business Goals are the ones that aren't too abstract, nor too specific, the ones that outline strategy but not tactics. For instance, the goal  of  "Rewarding loyal customers" can be framed as "Increase sales by rewarding loyal customers".  "Increasing sales" is the end-goal and "rewarding loyal customers" is the strategy.  If the goal was simply "Increase sales" or "reduce costs" then it would ultimately fall to whoever happened to implement that goal, a business analyst or -shock, horror- a software developer to determine what the best strategy would be.  The best people to define the strategy behind the goals are the Non-Acting Stakeholders, the directors, business owners and senior managers. &lt;/p&gt;

&lt;p&gt;On the flip-side, should the Goal become something like "Increase sales by rewarding loyal customers by giving them free gift-bags if they spend over $500 in a single transaction"  then we are mixing strategy with tactics.  This can be a Bad Thing, as creating tactics requires a level of domain, system and architectural knowledge that is usually lacking in Non-Acting Stakeholders (and I'm saying this in the best possible way). For example, the director or sales manager specifying the 'free gift-bag' ploy may not be aware of any logistics or distribution problems in delivering that many gift-bags. Tactics are better developed by other Stakeholder participation and should be captured separately as system Capabilities. &lt;/p&gt;

&lt;h2&gt;
  
  
  Capabilities
&lt;/h2&gt;

&lt;p&gt;A Capability is a system ability that enables the Stakeholders to achieve a Business Goal. Capabilities define &lt;em&gt;what&lt;/em&gt; the system does but not &lt;em&gt;how&lt;/em&gt; it does it.  They are high-level, coarse-grained descriptions of activities which enable Stakeholders to derive benefit from the system. A Capability can only be seen within the context of a Business Goal and its associated Stakeholders.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Examples&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The HR staff are able to use the system for the on-boarding of new employees&lt;/li&gt;
&lt;li&gt;The Seller can use the system to see real-time stock levels&lt;/li&gt;
&lt;li&gt;End-users are able to log onto the system using their social media accounts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our ultimate job as System Builders is to deliver Capabilities to our Stakeholders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;A Feature describes a system behaviour. It's closely associated with a Capability and answers the question "How are we going to deliver this Capability?"&lt;/p&gt;

&lt;p&gt;At a minimum, a Feature consists of a feature Title and a Scenario. A Scenario is a look at the Feature's functionality from a specific perspective. So if our Feature is titled 'Withdraw cash from cash-point machine', for instance, we could have Scenarios like 'Cash demand exceeds account balance' or 'Bank note denominations don't match requested amount'.&lt;/p&gt;

&lt;p&gt;However, having just a Title and a scenario doesn't help us describe the functionality much. A fully-featured Feature (excuse the pun) will include:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Feature Title&lt;/strong&gt;​: a brief description, e.g. Cash Withdrawal&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User Story&lt;/strong&gt;: we use the following template&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;As an [Actor]
I want [specific System Behavior]
So as to achieve [a goal contributing to a Capability]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: ​A link to the impact map this Feature relates to (more on these in the next post)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;​: any other text that helps the reader better understand the Feature&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt;: ​ (if applicable): a common prerequisite&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scenarios&lt;/strong&gt;​: are written in a structured manner&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Given [a Condition]
When [an Action is invoked]
Then [an expected Outcome occurs]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A valid Feature must have at least one defined Scenario.&lt;/p&gt;

&lt;p&gt;To derive Features we have to look at our system Capabilities and answer the question: 'What functionality must we implement to deliver this Capability?'.   &lt;/p&gt;

&lt;p&gt;Let's look at the: "The Seller can use the system to see real-time stock levels" Capability. How can we deliver this Capability? We could start by implementing these Features: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature 1: Integrate Warehouse API to our system&lt;/li&gt;
&lt;li&gt;Feature 2: Create notification mechanism for warehouse stock changes&lt;/li&gt;
&lt;li&gt;Feature 3: Build UI that shows current stock levels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Obviously there will be more Features than these in a real-life system, but hopefully you get the gist: Features are about implementing functionality, Capabilities are about Stakeholder ability to achieve a goal.  We'll be discussing these more in later posts in this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Stakeholders, Business Goals, Capabilities and Features are fundamental entities in the Requirements and Specification domain. In the next post, we'll explore using these entities to model our system Requirements and derive Specifications with a technique called Impact Mapping.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>agile</category>
      <category>requirements</category>
      <category>specifications</category>
    </item>
    <item>
      <title>So, do you know how to build a software system for your client? (part 1)</title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Sun, 23 Jun 2019 21:56:36 +0000</pubDate>
      <link>https://dev.to/redfred7/so-do-you-know-how-to-build-a-software-system-for-your-client-part-1-2cnp</link>
      <guid>https://dev.to/redfred7/so-do-you-know-how-to-build-a-software-system-for-your-client-part-1-2cnp</guid>
      <description>&lt;p&gt;You're a good developer. May be even a great one. You know your programming language(s) inside out. You're a wizard with databases and key-value stores. You're at ease with functional design as well as OO one. You can Dockerize anything and deploy it to AWS in a jiffy. &lt;/p&gt;

&lt;p&gt;...&lt;em&gt;but can you build a software system for your client??&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The reason I'm asking is because even though you may have all the skills mentioned above, that doesn't mean you can build a software system for your client. Sure, you can build &lt;em&gt;a software system&lt;/em&gt;, but by 'software system for your client' I mean a system that does, at a  minimum, what your client needs and, ideally, what they want.    &lt;/p&gt;

&lt;p&gt;"Sure", you say, "my client tells me what they want and I build it!". Really?! How do you know that you're building what they want? Yes, they tell you what they want but how do you know that this is what you're delivering? How do you know you even understand what they're telling you? More importantly, how does your client know that you're building what &lt;em&gt;they&lt;/em&gt; want?  The thing is, building software is more than just coding. Yes, coding is a crucial part of building a system but it's not the only part and -arguably- not even the most important.&lt;/p&gt;

&lt;p&gt;There have been &lt;a href="https://medium.com/specstimate/10-reasons-why-software-development-projects-fail-7200e7c9ae2e"&gt;many studies&lt;/a&gt; conducted on the &lt;a href="https://headchannel.co.uk/6-reasons-why-software-development-projects-fail"&gt;causes of software project failure&lt;/a&gt;. They all agree on one thing: The main cause of failure is a combination of requirements ambiguity, poor communication and unrealistic expectations. Let me summarise this in a sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If your project fails, chances are it will fail because you didn't properly manage its requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Managing requirements involves the elicitation, modelling, analysing and communicating of your client's requirements. That's right, you can be be the best coder in the world, head of a team of super coders but that will not make a big dent in the odds of your project failing. So I'll ask again: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do you know how to build a software system for your client?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By now you should have caught on to the fact that unless you know how to handle your client's requirements, your answer to the above question should be a resounding NO. Because you may be a great Developer but unless you can manage requirements you're no System Builder!&lt;/p&gt;

&lt;p&gt;So, where do you start? You start at the beginning: By understanding what Requirements and Specifications are. &lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements vs Specifications
&lt;/h2&gt;

&lt;p&gt;Even after more than two decades in software development I still get surprised by how many people, even experienced professionals, don't know the difference. They often use the terms interchangeably. This is the road to abject project failure.&lt;/p&gt;

&lt;p&gt;Simply put, a &lt;strong&gt;Requirement&lt;/strong&gt; is a stakeholder's expression of a need, wish or desire about the system being built. Requirements come in different shapes, forms and sizes and from various sources. Requirements can be provided as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;formal statements (“The system shall provide a document searching facility”)&lt;/li&gt;
&lt;li&gt;rules (”accounts with monthly deposits larger than $1000 receive a 10% discount”)&lt;/li&gt;
&lt;li&gt;examples (“Joe didn’t have to pay for his coffee because it was his 11th coffee in that store”)&lt;/li&gt;
&lt;li&gt;User Stories&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.heflo.com/blog/bpm/business-processes-definition/"&gt;Business Processes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Flow-charts, activity charts, or other types of diagram&lt;/li&gt;
&lt;li&gt;some other obscure way that I can’t even imagine at this point&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;strong&gt;Specification&lt;/strong&gt;, on the other hand, is a description of the system behaviour required in order to fulfil or realise a Requirement. So for our “The system shall provide a document searching facility” above, a specification could be something along the lines of specifying how to add some search strings on a search bar. The Specification is just a way of defining how we’re going to realise the Requirement. Nothing more, nothing less.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Requirements gap
&lt;/h2&gt;

&lt;p&gt;So let's get this absolutely straight: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Requirements tell us what our client wants or needs. Ultimately, it is our client's responsibility to provide us with the requirements. What we can do is help them identify, refine and validate them. There are a number of techniques to help us achieve that and they will be covered in the next posts of this series.
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Specifications tell our client (and our team) how we will meet these requirements in the delivered system. Specifications are essential to building a successful system. They drive the whole development and testing process. To quote &lt;a href="https://www.joelonsoftware.com/2000/10/02/painless-functional-specifications-part-1-why-bother/"&gt;Joel Spolsky&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Failing to write a spec is the single biggest unnecessary risk you can take in a software project. It’s as stupid as setting off to cross the Mojave desert with just the clothes on your back, hoping to 'wing it'." &lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Our job as System Builders is to elicit our client's Requirements and translate them to Specifications. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Our client's job is to confirm that the Specifications reflect their Requirements.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This series aims to outline a number of agile methods and techniques that will help you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elicit requirements&lt;/li&gt;
&lt;li&gt;Model them&lt;/li&gt;
&lt;li&gt;Create executable specifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The gap between Requirements and Specifications is a deep and dangerous one. The next article will show you how to start building a bridge over it, so stay tuned.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PS&lt;/em&gt;: the articles in this series are related to, and a continuation of, my earlier &lt;a href="https://dev.to/redfred7/enough-with-the-user-stories-already-2a8a"&gt;post on User Stories&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>agile</category>
      <category>requirements</category>
      <category>specifications</category>
    </item>
    <item>
      <title>Enough with the User Stories already!</title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Tue, 02 Apr 2019 11:07:13 +0000</pubDate>
      <link>https://dev.to/redfred7/enough-with-the-user-stories-already-2a8a</link>
      <guid>https://dev.to/redfred7/enough-with-the-user-stories-already-2a8a</guid>
      <description>&lt;h3&gt;
  
  
  The fog of Agile
&lt;/h3&gt;

&lt;p&gt;Agile, at its core, is a set of &lt;a href="https://agilemanifesto.org/principles.html" rel="noopener noreferrer"&gt;guiding principles&lt;/a&gt; that are interpreted by the creators and practitioners of various processes, methods and frameworks.  Like a language with many dialects, this results in the same word meaning slightly different things in each dialect. Asking the question "what's a user story?" will give you a dozen different answers. A user story can be a requirement, a feature, a description, an end-goal, a high-level abstraction, a small piece of business value and many other things, depending on who you ask - and &lt;em&gt;when&lt;/em&gt; you ask them ;) &lt;/p&gt;

&lt;p&gt;I tend to agree with &lt;a href="https://www.mountaingoatsoftware.com/agile/user-stories" rel="noopener noreferrer"&gt;Mike Cohn's definition&lt;/a&gt;: A user story is a description of a feature. Of course, this only moves the question to "what is a feature?". A quick Googling shows us that definitions abound: a feature is a functionality, a capability, a requirement, a client-valued function expressed in the form , a part of an Epic, etc. And so it goes on and on.&lt;/p&gt;

&lt;p&gt;Here's a common pattern I've come across many, many times and I'm willing to bet that you have too:  A stakeholder asks to look at the 'user stories'. The stakeholder later returns complaining that the user stories are too technical or too abstract or that the wrong actors are involved &lt;/p&gt;

&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;As an end-user 
I want to log-in with my social media credentials
so I don't have to remember extra usernames and passwords
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a user story, right? You can give this to your stakeholder but they'll probably complain that this is too generic to be of any value. And they'll be right.  You then give them this story:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;As an end-user
When I log-in I want to be re-directed to the login page of my selected Identity Provider
So that, upon entering my credentials, the IP can redirect me back to our system with a valid SAML token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stakeholder looks at you blankly. You don't understand what the problem is. They wanted User Stories, you gave them User Stories, so WTF? The thing is, the stakeholders want something very specific but they don't have the right words to express it because User Stories is a loaded term. Let's hold that thought for a minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Requirements Domain Model
&lt;/h3&gt;

&lt;p&gt;In his &lt;a href="https://www.manning.com/books/bdd-in-action" rel="noopener noreferrer"&gt;BDD in Action&lt;/a&gt; book, John Ferguson Smart elaborates on the usage of Impact Maps, a technique introduced by &lt;a href="https://www.impactmapping.org/index.html" rel="noopener noreferrer"&gt;Gojko Adzic&lt;/a&gt; that is useful for answering the big questions of the Requirements and Specifications model: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why are we building the system?&lt;/li&gt;
&lt;li&gt;Who benefits from it? &lt;/li&gt;
&lt;li&gt;What do we need to build?&lt;/li&gt;
&lt;li&gt;How will we build it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These can be directly translated to Requirements and Specifications Domain Entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business Goal (Why): The intended benefit of our system&lt;/li&gt;
&lt;li&gt;Stakeholder (Who): Someone who is affected by our system&lt;/li&gt;
&lt;li&gt;Capability (What): A system ability that enables the Stakeholders to achieve a business goal&lt;/li&gt;
&lt;li&gt;Feature (How): A functionality that helps deliver a Capability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And, just like that,  we have well-defined Domain Entities. Our requirements are represented by Capabilities in the context of Stakeholders and Business Goals.  Our specifications are the Features, which describe the system functionality we will implement in order to deliver the system's Capabilities. In true BDD fashion, a Feature is described by a User Story followed by a number of scenarios/acceptance criteria. A Feature &lt;strong&gt;is not a User Story&lt;/strong&gt;! We can use User Stories to describe Capabilities, Features and Business Goals too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffiledn.com%2FlBg7bSLnQRakb21qRGSa3pS%2FImpact%2520Map%2520social%2520login.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffiledn.com%2FlBg7bSLnQRakb21qRGSa3pS%2FImpact%2520Map%2520social%2520login.png" alt="Impact Map"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Our system Requirements are the Capabilities in the context of the beneficiary Stakeholders and valid Business Goals&lt;/li&gt;
&lt;li&gt;We can describe Capabilities and Business Goals with User Stories. This doesn't mean that the stories are these things. &lt;/li&gt;
&lt;li&gt;Our Specifications are descriptions of the system behaviour required in order to deliver a Capability, i.e. the Features&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  User Stories are just descriptive devices for Requirements Domain Entities.
&lt;/h3&gt;

&lt;p&gt;So let's jump back to our confused stakeholder. When they asked you for User Stories, what they were asking for was &lt;em&gt;the Features&lt;/em&gt;! They wanted a description of the system functionality that you'll provide in order to enable them to achieve their goals. Nothing more, nothing less. User Stories are not Domain Entities, they are just descriptors. We need to start communicating using Domain Entities, not descriptors. &lt;/p&gt;

&lt;p&gt;So we provide the following to our stakeholder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="kd"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Login with Twitter
As a end-user with a Twitter account
I want to log-in with my Twitter credentials
so I don't have to know extra usernames and passwords

  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; First-time login with Twitter, already signed-on
    &lt;span class="nf"&gt;Given &lt;/span&gt;the user visits our system's login page
    &lt;span class="nf"&gt;And &lt;/span&gt;the user hasn't signed-on our system with Twitter before
    &lt;span class="nf"&gt;And &lt;/span&gt;the user is not already signed onto Twitter
    &lt;span class="nf"&gt;When &lt;/span&gt;the user chooses to sign-on our system with Twitter credentials
    &lt;span class="nf"&gt;Then &lt;/span&gt;the user is presented with a Twitter login page

  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; First-time login with Twitter, not currently signed-on
    &lt;span class="nf"&gt;Given &lt;/span&gt;the user visits our system's login page
    &lt;span class="nf"&gt;And &lt;/span&gt;the user hasn't signed-on our system with Twitter before
    &lt;span class="nf"&gt;And &lt;/span&gt;the user is already signed onto Twitter
    &lt;span class="nf"&gt;When &lt;/span&gt;the user chooses to sign-on our system with Twitter credentials
    &lt;span class="nf"&gt;Then &lt;/span&gt;the user is presented with a Twitter permissions and confirmation page

&lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="err"&gt;(more&lt;/span&gt; &lt;span class="err"&gt;scenarios)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how we present a User Story beneath our Feature title in order to best describe and illustrate our Feature. Also note that our Feature would &lt;em&gt;still be valid and usable even if we didn't use a User Story&lt;/em&gt;. The User Story is not the Feature! It just gives us a nice narrative so it's useful to use it in our Feature. But that is all.  We're not creating User Stories, we're defining Features.   &lt;/p&gt;

&lt;p&gt;As Developers, Product Owners or Business Analysts we get flooded with our clients' wishes, needs and desires. Our first question should be: "What should the stakeholders be able to do with our system in order to fulfil their wish or need?". Once we've answered that, our next question should be "How are we going to deliver that capability?". The result of this thought process will give us a set of Specifications (Features) that realise the stakeholders' requirements towards achieving some Business Goals. So let's clear the fog and start talking more about Capabilities and Features, instead of their generic and variant descriptors.  Enough with User Stories already! &lt;/p&gt;

</description>
      <category>agile</category>
      <category>userstories</category>
      <category>bdd</category>
      <category>requirements</category>
    </item>
    <item>
      <title>Parallelising ETL workflows with the Jongleur gem </title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Mon, 14 Jan 2019 09:39:14 +0000</pubDate>
      <link>https://dev.to/redfred7/parallelising-etl-workflows-with-the-jongleur-gem--4cnp</link>
      <guid>https://dev.to/redfred7/parallelising-etl-workflows-with-the-jongleur-gem--4cnp</guid>
      <description>&lt;p&gt;&lt;a&gt; &lt;img src="/assets/img/jongleur_etl.png" alt="ETL with Jongleur"&gt; &lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Our assignment
&lt;/h2&gt;

&lt;p&gt;Our company has just gone on a huge recruitment spree and has just hired 100,000 new employees. The HR department has sent us a spreadsheet with the employee details, asking us to save them in the company staff database. After having a look at the data, we notice a few actions we need to take before inserting the details into our database and we come up with the following algorithm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read data from the spreadsheet&lt;/li&gt;
&lt;li&gt;Break employees' full name into first name and last name fields&lt;/li&gt;
&lt;li&gt;Assign each employee a unique company number &lt;/li&gt;
&lt;li&gt;Insert employee into the database as long as their first and last name is less than 255 characters long&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A single-process ETL pipeline
&lt;/h2&gt;

&lt;p&gt;We then proceed to write a crude but effective ETL pipeline&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'csv'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'pg'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'ostruct'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'securerandom'&lt;/span&gt;

&lt;span class="vi"&gt;@users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="vi"&gt;@conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="ss"&gt;dbname: &lt;/span&gt;&lt;span class="s1"&gt;'testdb'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;DATA_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"data/users.csv"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;CSV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenStruct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@users&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;
  &lt;span class="vi"&gt;@users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map!&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;staff_no&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;
    &lt;span class="n"&gt;usr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;  &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;"INSERT INTO STAFF (FIRST_NAME, LAST_NAME, EMAIL, STAFF_NO) VALUES ($1, $2, $3, $4)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;staff_no&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Validation failed for &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;transform&lt;/span&gt;
&lt;span class="vi"&gt;@users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;extract&lt;/code&gt; method reads the data from file and stores it in memory. The &lt;code&gt;transform&lt;/code&gt; method then splits the full name field into first and last name and assigns a UUID to the employee. Finally, our &lt;code&gt;load&lt;/code&gt; method ensures the name fields don't exceed the expected length, before inserting the data into the Staff table.&lt;/p&gt;

&lt;p&gt;We test our script and it works fine, however all this data crunching takes a long time and, us being the perfectionist that we are, would like to make it faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  The need for speed
&lt;/h2&gt;

&lt;p&gt;Enter &lt;a href="https://gitlab.com/RedFred7/Jongleur" rel="noopener noreferrer"&gt;Jongleur&lt;/a&gt; a Ruby gem that allows us to create tasks that are executed as separate OS processes and that can be ran with a certain precedence.&lt;/p&gt;

&lt;p&gt;We'll use Jongleur to split our ETL pipeline into two separate and parallel pipelines, each dealing with one half of the input data. We want our task graph to look like this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffiledn.com%2FlBg7bSLnQRakb21qRGSa3pS%2Fjongleur_etl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffiledn.com%2FlBg7bSLnQRakb21qRGSa3pS%2Fjongleur_etl.png" alt="2x ETL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each Extract, Transform and Load task will do exactly what we described in our initial algorithm above, but with only half of the spreadsheet records. The Cleanup task will kick-in last, to sweep away any undue remnants after our pipeline has been ran.&lt;/p&gt;

&lt;p&gt;Representing the above graph in Jongleur is a simple matter of creating a Ruby Hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;etl_graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="no"&gt;Extract1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Transform1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="no"&gt;Transform1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Load1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="no"&gt;Extract2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Transform2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="no"&gt;Transform2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Load2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="no"&gt;Load1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Cleanup&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="no"&gt;Load2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Cleanup&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then just tell Jongleur to use our task graph&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Jongleur&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task_graph&lt;/span&gt; &lt;span class="n"&gt;etl_graph&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Design considerations
&lt;/h2&gt;

&lt;p&gt;One thing we need to consider before creating our parallel tasks is how to share data between tasks which are ran as separate processes. Shared Memory could be used but has some &lt;a href="https://brandur.org/ruby-memory" rel="noopener noreferrer"&gt;pitfalls&lt;/a&gt; which is why we'll be using the wonderful in-memory data store which is &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt;. Redis is (mostly) single-threaded, which means concurrent requests are executed sequentially and safely (and blazingly quickly). Furthermore, we'll take advantage of one of Jongleur's great features whereby each task can access the process ids (pids) of its predecessors. This means that we can use a task's pid as a key for its data. So our Extract1 task can save data to Redis with its own pid and when Transform1 task starts it will know exactly which data was created by Extract1, its predecessor!&lt;/p&gt;

&lt;h2&gt;
  
  
  Common attributes
&lt;/h2&gt;

&lt;p&gt;Every Jongleur task class must inherit from the base class &lt;code&gt;Jongleur::WorkerTask&lt;/code&gt;. This means that we can use this class to define data that we want accessible from any and all WorkerTask instances.  In our case we want any Extract, Transform and Cleanup tasks to be able to access our Redis data store. To achieve that we simply define the Redis connection as a class variable in the  &lt;code&gt;Jongleur::WorkerTask&lt;/code&gt; hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Jongleur::WorkerTask&lt;/span&gt;
  &lt;span class="vc"&gt;@@redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;port: &lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;db: &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extracting
&lt;/h2&gt;

&lt;p&gt;Both our Extract1 and Extract2 tasks will need to have some common functionality, namely to parse records from the csv file and save them to Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Extract&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Jongleur&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WorkerTask&lt;/span&gt;

  &lt;span class="no"&gt;DATA_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"data/users.csv"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rowno&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rowno&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;

    &lt;span class="vc"&gt;@@redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"user:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rowno&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the only divergence between Extract1 and Extract2 comes when reading the input data from the csv file. Extract1 will only read and store the first 50,000 records, while Extract2 will read and store the remaining 50,000 records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Extract1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Extract&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;
    &lt;span class="no"&gt;CSV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rowno&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rowno&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;50000&lt;/span&gt;
      &lt;span class="n"&gt;process_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rowno&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Extract2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Extract&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;
    &lt;span class="no"&gt;CSV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rowno&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rowno&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;50000&lt;/span&gt;
      &lt;span class="n"&gt;process_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rowno&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Transforming
&lt;/h2&gt;

&lt;p&gt;Each Tranform task will need to use its Extracting predecessor's pid in order to load the extracted records it needs to transform. It will then proceed to split each record's name, assign it a UUID and save it back to Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Transform&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Jongleur&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WorkerTask&lt;/span&gt;
  &lt;span class="vi"&gt;@desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Transforming'&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;
    &lt;span class="n"&gt;loader_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Jongleur&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Implementation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_process_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predecessors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vc"&gt;@@redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hgetall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loader_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;usr_key&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;transformed_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
      &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;usr_key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;transformed_user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;transformed_user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:last_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;transformed_user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:staff_no&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;
      &lt;span class="n"&gt;transformed_user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'num'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;transformed_user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="vc"&gt;@@redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"user:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'num'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transformed_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Transform1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Transform2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Loading
&lt;/h2&gt;

&lt;p&gt;Load1 and Load2 will pick up the records stored by their respective predecessor Transform tasks, will validate the name length and will insert the records in the Staff table of our HR's Postgres database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Load&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Jongleur&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WorkerTask&lt;/span&gt;
  &lt;span class="vi"&gt;@desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Loading'&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;
    &lt;span class="n"&gt;loader_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Jongleur&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Implementation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_process_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predecessors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vc"&gt;@@redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hgetall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loader_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="ss"&gt;dbname: &lt;/span&gt;&lt;span class="s1"&gt;'testdb'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;usr_key&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;usr_key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'first_name'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;  &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'last_name'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;"INSERT INTO STAFF (FIRST_NAME, LAST_NAME, EMAIL, STAFF_NO) VALUES ($1, $2, $3, $4)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'first_name'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'last_name'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'staff_no'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Validation failed for &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Load1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Load2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;All our Cleanup task has to do is to erase the Redis data created by the previously-ran tasks&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cleanup&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Jongleur&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WorkerTask&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;
    &lt;span class="vc"&gt;@@redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flushdb&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"...Cleanup"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running our pipelines
&lt;/h2&gt;

&lt;p&gt;We now have &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Defined our Task Graph and configured Jongleur with it&lt;/li&gt;
&lt;li&gt;Implemented an &lt;code&gt;#execute&lt;/code&gt; method for each of the Tasks in our Task Graph&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We are now ready to invoke Jongleur and run our parallel pipelines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;completed&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;task_matrix&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Jongleur run is complete &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;task_matrix&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"oh-oh"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failed_tasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_matrix&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Starting workflow...
starting task Extract1
starting task Extract2
finished task: Extract1, process: 22722, exit_status: 0, success: &lt;span class="nb"&gt;true
&lt;/span&gt;starting task Transform1
finished task: Extract2, process: 22723, exit_status: 0, success: &lt;span class="nb"&gt;true
&lt;/span&gt;starting task Transform2
finished task: Transform1, process: 22726, exit_status: 0, success: &lt;span class="nb"&gt;true
&lt;/span&gt;starting task Load1
finished task: Transform2, process: 22727, exit_status: 0, success: &lt;span class="nb"&gt;true
&lt;/span&gt;starting task Load2
finished task: Load1, process: 22729, exit_status: 0, success: &lt;span class="nb"&gt;true
&lt;/span&gt;finished task: Load2, process: 22732, exit_status: 0, success: &lt;span class="nb"&gt;true
&lt;/span&gt;starting task Cleanup
...Cleanup
finished task: Cleanup, process: 22745, exit_status: 0, success: &lt;span class="nb"&gt;true
&lt;/span&gt;Workflow finished
Jongleur run is &lt;span class="nb"&gt;complete&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;struct Jongleur::Task name=:Extract1, pid=22722, running=false, exit_status=0, finish_time=1546800512.6342049, success_status=true&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;struct Jongleur::Task name=:Transform1, pid=22726, running=false, exit_status=0, finish_time=1546800518.355283, success_status=true&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;struct Jongleur::Task name=:Extract2, pid=22723, running=false, exit_status=0, finish_time=1546800512.967079, success_status=true&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;struct Jongleur::Task name=:Transform2, pid=22727, running=false, exit_status=0, finish_time=1546800519.273847, success_status=true&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;struct Jongleur::Task name=:Load1, pid=22729, running=false, exit_status=0, finish_time=1546800533.147315, success_status=true&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;struct Jongleur::Task name=:Load2, pid=22732, running=false, exit_status=0, finish_time=1546800534.1630201, success_status=true&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;struct Jongleur::Task name=:Cleanup, pid=22745, running=false, exit_status=0, finish_time=1546800534.899818, success_status=true&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jongleur gives us a thorough account of each Task's details and success status. Notice how both initial Tasks (Extract1, Extract2) start at the same time and each new Task starts only once its predecessor on the Task Graph has finished. If a Task failed, for whatever reason, Jongleur wouldn't execute its dependent tasks but it would continue running any other tasks it could. The status of all Tasks would still be visible in the Task Matrix structure yielded by the &lt;code&gt;completed&lt;/code&gt; callback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;Running the simple single-process ETL pipeline at the beginning of this article produced the following timings:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;9.55s user 1.40s system 32% cpu 33.494 total&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Running the parallel Jongleur pipelines produced:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;19.99s user 5.23s system 89% cpu 27.210 total&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;User time is increased with Jongleur (I'd guess due to using Redis) and so is kernel time, obviously due to forking and managing many processes, but overall time is approx. 18% faster using Jongleur with two pipelines. Also consider that in this example we only used two parallel lines, i.e. we split the data load into 2 parts. It is a simple matter to split it into 4, 8 or even 16 parallel lines (dependent on our CPU cores) and thus gain a massive performance gain, using the same logic and principles demonstrated here and simply adding more Tasks.                &lt;/p&gt;

&lt;h2&gt;
  
  
  Jongleur advantages
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Performance, through parallelisation.&lt;/li&gt;
&lt;li&gt;Implicit precedence handling. Jongleur will start running a task only once its predecessor tasks have successfully finished running. If a task fails, its dependent tasks wil not be ran.&lt;/li&gt;
&lt;li&gt;Code modularisation. If a task fails, for whatever reason, it will be marked as such by Jongleur and its dependent tasks will not be run. However, that won't stop any other tasks from being run, which means that partial failure does not necessitate complete failure, in use cases where this is desirable. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Complete Code
&lt;/h2&gt;

&lt;p&gt;including data file for this demo is &lt;a href="https://gitlab.com/RedFred7/jongleur_etl" rel="noopener noreferrer"&gt;freely available&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future
&lt;/h2&gt;

&lt;p&gt;Feel free to suggest improvements and new features for Jongleur or to talk to me about trying it out for your own Use Cases :)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="http://bootstrap.me.uk" rel="noopener noreferrer"&gt;http://bootstrap.me.uk&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>etl</category>
      <category>parallelisation</category>
      <category>multiprocessing</category>
    </item>
    <item>
      <title>The Three Ghosts of FizzBuzz: A Christmas Story</title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Fri, 21 Dec 2018 15:07:15 +0000</pubDate>
      <link>https://dev.to/redfred7/the-three-ghosts-of-fizzbuzz-a-christmas-story-1ban</link>
      <guid>https://dev.to/redfred7/the-three-ghosts-of-fizzbuzz-a-christmas-story-1ban</guid>
      <description>&lt;p&gt;FizzBuzz is a simple kids game, often used as a test at programming interviews.  It goes like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Write a program that prints the numbers from 1 to 100. But for  multiples of 3 print “Fizz” instead of the number and for multiples of 5 print “Buzz”. For numbers which are multiples of both 3 and 5 print “FizzBuzz”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I fell asleep yesterday while thinking about all the different ways we can solve FizzBuzz in Ruby. Then three ghosts appeared in my dream. The first one was the Ghost of Imperative Programming (GImP, for short). It showed me how to do FizzBuzz in an imperative way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;imperative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="s1"&gt;'Fizz'&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="s1"&gt;'Buzz'&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="s1"&gt;'FizzBuzz'&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Simple conditionals are easy to read", it said. "They're also relatively fast. They're a good, basic way to implement your solution" &lt;/p&gt;

&lt;p&gt;Next, the Ghost of Antecedent Declarative Inference appeared (GhAnDI). "Don't listen to GImP" it said. "Be more clever. Look here, the conditions that are evaluated to produce each FizzBuzz value are mutually exclusive, if you represent them as Hash values then only a maximum of &lt;strong&gt;one&lt;/strong&gt; of our Hash keys will have the value of 'true' for any given number. Use this to your advantage."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;declarative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'Fizz'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'Buzz'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'FizzBuzz'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"You then make sure you return this 'true' key (&lt;code&gt;h.key(true)&lt;/code&gt;) from the method. Of course if all the keys have the 'false' value (i.e. the passed number is neither divisible by 3, nor by 5) then &lt;code&gt;h.key(true)&lt;/code&gt; will evaluate to nil, so use the logical OR operator to ensure that -in this case- you return the actual passed number." &lt;/p&gt;

&lt;p&gt;"That's pretty smart" I said. "We are not specifying a sequence of steps leading to the solution, but rather the conditions required and we leverage the language features to work out the correct one. Cool!" &lt;/p&gt;

&lt;p&gt;As GhAnDI departed, I sensed a new presence arriving. It was wearing trendy clothes , had a well-oiled beard and there was a large and vocal group of developers following it around. It was... the Ghost of Functional Erudition (GoFEr).&lt;/p&gt;

&lt;p&gt;"The other two are the past", GoFEr said. "I will show you the future"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;functional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'FizzBuzz'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Buzz'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Fizz'&lt;/span&gt;&lt;span class="p"&gt;]].&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)}.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"We specify both conditions and outcome together" it said. "Ruby doesn't have tuples, but we can get a similar effect with an Array of Arrays. We then select the 'tuple' which satisfies the condition of zero-mod division with the tuple's divisor (&lt;code&gt;x[0]&lt;/code&gt;). If more than one tuples match, we only select the first one, since we cleverly ordered our Array that way. And if there are no matches then we return the   original number input. Simples."&lt;/p&gt;

&lt;p&gt;And with that GoFEr shimmied away to the sounds of 'California dreaming' and I woke up. I learned some useful techniques from this encounter, but mostly I came to appreciate just how powerful and flexible the Ruby language really is. &lt;/p&gt;

&lt;p&gt;Merry Christmas everyone and happy Ruby-ing! &lt;/p&gt;

&lt;p&gt;&lt;em&gt;PS&lt;/em&gt;: As a Christmas present, the Ghosts put all their code along with some bench-marking on &lt;a href="https://gitlab.com/snippets/35596"&gt;https://gitlab.com/snippets/35596&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>fizzbuzz</category>
      <category>interview</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Truth tables as Ruby Hashes</title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Mon, 17 Dec 2018 08:20:46 +0000</pubDate>
      <link>https://dev.to/redfred7/truth-tables-as-ruby-hashes-18e0</link>
      <guid>https://dev.to/redfred7/truth-tables-as-ruby-hashes-18e0</guid>
      <description>

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Truth_table"&gt;Truth tables&lt;/a&gt; are a common way of defining and testing code behaviour. A truth table treats a component or function of our system as a black box, with well-defined inputs and outputs. At the same time, &lt;a href="https://docs.ruby-lang.org/en/2.5.0/Hash.html"&gt;Ruby Hashes&lt;/a&gt; are flexible enough that they can be used as decision objects, receiving an input (key) and determining its value. Hopefully you can see where I'm going with this ;) Let's look at an example. The problem in question is this: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Write a method that receives the month and the year and outputs how many days there are in that month".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds easy, doesn't it? We all know which months have 30 and which 31 days in them. Apart from February, that is. February usually has 28 days, except that in leap years it has 29.  How do we know which years are leap years? There are certain rules that allow us to determine leap years:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The year is evenly divisible by 4&lt;/li&gt;
&lt;li&gt;If the year can be evenly divided by 100, it is NOT a leap year, unless&lt;/li&gt;
&lt;li&gt;the year is also evenly divisible by 400. Then it is a leap year&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To sum it up, a year is a leap year when&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;it is evenly divided by 4 but NOT evenly divided by 100.&lt;/li&gt;
&lt;li&gt;It is evenly divided by 4, 100 AND 400.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The truth table for this problem would be:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Month &lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;No of Days&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;jan, mar, may, jul, aug, oct, dec&lt;/td&gt;
&lt;td&gt;any&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apr, jun, sep, nov&lt;/td&gt;
&lt;td&gt;any&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;feb&lt;/td&gt;
&lt;td&gt;year % 4 != 0&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;feb&lt;/td&gt;
&lt;td&gt;(year % 4 == 0) &amp;amp;&amp;amp; (year % 100 != 0)&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;feb&lt;/td&gt;
&lt;td&gt;(year % 4 == 0) &amp;amp;&amp;amp; (year % 100 == 0)  &amp;amp;&amp;amp; (year % 400 != 0)&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;feb&lt;/td&gt;
&lt;td&gt;(year % 4 == 0) &amp;amp;&amp;amp; (year % 100 == 0) &amp;amp;&amp;amp; (year % 400 == 0)&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now we could use a multi-branch conditional or maybe a &lt;em&gt;Case&lt;/em&gt; statement to implement this truth table. But there's another way. We can leverage two powerful Ruby features to model our truth table as a Hash:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Everything is an expression in Ruby.  I mean everything, and that includes Hash keys and values. Every statement gets evaluated to an object and that's a beautiful thing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ruby is great for &lt;a title="" href="http://en.wikipedia.org/wiki/List_comprehension"&gt;List Comprehensions&lt;/a&gt;. It offers some great ways of making lists out of lists, either in an iterative or a functional manner.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Knowing all this, we can write our method as follows:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;month_days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sx"&gt;%w(jan mar may jul aug oct dec)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sx"&gt;%w(apr jun sep nov)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sx"&gt;%w(feb)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                    &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
                    &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# find the Hash key the includes the required month, return its value &lt;/span&gt;
    &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We use Arrays for the Hash keys and we use the ternary operator as  a value for the &lt;em&gt;february&lt;/em&gt; key.  Our returning object is the value of a Hash key that is generated by filtering the original Hash's keys (Arrays) based on the desired month. Let's run it:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; puts month_days 1900, &lt;span class="s1"&gt;'feb'&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 28
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; puts month_days 2000, &lt;span class="s1"&gt;'feb'&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 29
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; puts month_days 1900, &lt;span class="s1"&gt;'sep'&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 30
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Beautiful. This is much cleaner and elegant than a big If or Case statement. Moreover, a Hash can be easily memoized so that any intricate calculations become just a simple lookup and performance is boosted. Of course, Ruby being Ruby, there'll be a different or better way, so if you know of any feel free to share it with me by commenting below.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="http://bootstrap.me.uk/2015/04/10/truth-tables-using-ruby-hashes.html"&gt;Bootstrapped Thoughts&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;


</description>
      <category>ruby</category>
      <category>truthtable</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>  Python Slices vs Ruby blocks  </title>
      <dc:creator>Fred Heath</dc:creator>
      <pubDate>Tue, 11 Sep 2018 08:40:18 +0000</pubDate>
      <link>https://dev.to/redfred7/--python-slices-vs-ruby-blocks---5h3f</link>
      <guid>https://dev.to/redfred7/--python-slices-vs-ruby-blocks---5h3f</guid>
      <description>&lt;p&gt;A couple of my Python colleagues tried to impress me today with Python's named slices feature. The way it works is like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'helloworld!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'h'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'o'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'o'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'!'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;WORLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;WORLD&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'o'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'d'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you can have your own customised slicing mechanism that you can apply to any list. Which is kind of cool. That prompted me to demonstrate how we can do the same thing with Ruby. Luckily, in Ruby world we have blocks, procs and lambdas, the ultimate play-dough that allows us to stretch and flex in every direction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;​&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'h'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'o'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'o'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'!'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'h'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'o'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'o'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'!'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;world&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"l"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"!"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;​so we can do things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"h"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or even things that we can't do with Python's named slicing, since it doesn't allow us to pass the receiver as an argument to the block (x in the example below)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sx"&gt;%w(dog rabbit fox cat)&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"cat"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;median&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;median&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sx"&gt;%w(dog rabbit cat)&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"rabbit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and of course we're not just restricted to slicing arrays,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;domain_extractor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.+@([^.]+).+/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;domain_extractor&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fred@mydomain.co.uk"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"mydomain"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and since a block is just an anonymous Proc object, we can use it with any method that accepts Proc parameters&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;email_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fred@mydomain.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"john@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"mary@yahoo.co.uk"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fred@mydomain.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"john@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"mary@yahoo.co.uk"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;email_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;domain_extractor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mydomain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"gmail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"yahoo"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blocks and Procs (a.k.a lambdas) are ideal for some quick, reusable functionality or for when defining a full-blown method would be too much, but also for more serious uses such as callbacks and deferred execution.  IMHO, they are a fundamental part of what makes Ruby such a flexible and powerful language. Learn them, use them, enjoy them :)&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>python</category>
      <category>blocks</category>
      <category>lambdas</category>
    </item>
  </channel>
</rss>
