JavaDay Kyiv

Turns out Ukrainians spell Kiev as Kyiv. And I was lucky enough to travel there to speak at the JavaDay conference. 

Andrii and the entire JavaDay Kyiv team did a phenomenal job. There was even a live band! I'll post videos of my favorite talks as soon as they're up. 


The City

I was more than impressed with Kyiv and the people who live there. Though language was a struggle — and more than once I played charades with a stranger — everyone I interacted with was lovely and went out of their way to help. 



Kyiv is full of ancient and amazing churches. I stayed down the street from the Ukrainian Orthodox Church of the Kyivan Patriarchate, a beautiful church with stunning paintings delighting its walls. 

I was touched and impressed by the level of reverence displayed by visitors, so much that I felt compelled to cover my head with a scarf. 



Kiev Pechersk Lavra

The other highlight of the trip was Kiev Pechersk Lavra, a monastery founded in 1051 A.D. (or C.E.? What are we doing these days?)

While I didn't get to go into the caves for which the monastery is known, the churches and chapels above ground were beyond anything I've ever seen. Every wall was covered in ornately carved gold and paintings of saints. 

If you're ever in Kyiv, I highly recommend you visit it. 



My Jetsetting Daughter

I made the brave (stupid?) choice to bring my daughter along. She's obsessed with buses and trains so I heard a lot of "Bus!" and "Toot toot!" every time we boarded another one. 



Arsenalna Station

I accidentally stumbled into the deepest subway station in the world. Arsenalna station is over 346 feet deep and required a trip on two extremely long escalators which moved alarmingly fast. 

For my DC friends, not only is their subway called Metro as well, they don't tolerate escalefters! Finding common ground. This is how we solve world problems, folks. 

Ukrainian Beer


International Toilets

I absolutely love seeing the different styles of toilets that exist all over the world. So here's two I ran into during my visit. Yes, the one on the right is a squatty potty. That was interesting to say the least!


Humpty Dumpty + DevOps

I’m convinced Humpty Dumpty is a story of DevOps gone wrong. 

Humpty Dumpty sat on a wall,
Humpty Dumpty had a great fall.
All the king's horses and all the king's men
Couldn't put Humpty together again.

First, who asks a horse to do surgery? Hoofs can’t hold scalpels. Second, either the king’s men are inept or they’re not communicating. Two kindergarteners with some Elmer’s could have done the job.

You see, Humpty is a deploy. He was fine in staging but shit the bed in production. Now the site’s down and your boss is threatening everyone’s jobs. IT is saying the code is broken. The developers are saying it’s a server issue. 

Meanwhile, Humpty is bleeding out. And your customers are complaining on Twitter. Which means a customer service rep has entered the #incident channel to tell you the site’s down. Yea, no shit, Tom. 

Sound familiar? 

We’ve all been there. A deploy goes awry and the entire department is up in arms, defending themselves and blaming each other.

All The King’s Horses and All The King’s Men

So there we are. In chaos. The site’s down. The boss is pissed. 

I don’t know about you, but I always think best when my boss has morphed into Al Capone and I’m staring down the barrel of a metaphorical Tommy Gun.


At first glance, you might think we’ve assembled the best team to handle the crisis. There are engineers who know the code inside and out and an ops team that can handle any systems fire. 

Yet, it doesn’t work out that way. It always devolves into a blame game. You know the scene.

Developers say…

  • “It’s a server issue.” 
  • “My code worked in staging.”
  • “A configuration must have changed.”

Operations people say…

  • “Has to be a code change from the last deploy.”
  • “What was just deployed?...”
  • “Why are we the only ones that know what is broken?...”

All the king’s horses and all the king’s men aren’t working together.  

Let’s Stop Fighting

Ok, first, I’m not going to address all-out fist fights. Because honestly, if your department hosts a weekly version of Fight Club, you should really change jobs. 

I’m talking more about what could be described as friction, attitude, or a general inability to tolerate each other without eye rolls and audible sighs. What I like to call good ’ol Southern-style passive aggressiveness.

I’ll give you an example. 

I recently had some mild conflict with one of our DevOps guys. A day after a deploy, a feature on one of our sites — the ability for admins to upload new photos — wasn’t working. The user didn’t receive an error message after uploading. The new photo just didn’t show up. 

A project manager (PM) messaged me and an ops guy.

I had 20 minutes before another meeting — why are there so many meetings?! — so I felt a little pressure to locate the issue quickly. I should have recognized that I was on edge and ill prepared to deal with the situation at that moment.

But I didn’t. I’m human. 

To make matters worse, an eerily similar issue had come up during testing in QA. During that code hunt, the ops team lovingly implied that it was my problem. And after an hour or two of log reading and double-checking my work, I discovered it was in fact an ops issue. 

Not that it isn’t my fault sometimes. I make plenty of mistakes. And then I obsess about them for months...

Fast-forward two weeks and here we are again. So I’m primed and have plenty of attitude. My bad. 

I holler across the room to see what the logs say. 

“I don’t know.”

Um, wanna go look it up?! 

OK, I didn’t actually say that. But I’m 80% sure I got the message across with my eyes. 

So I track down the production logs while coordinating with the PM so I didn’t have to test the issue in production. 

Minutes pass and now I’m in my meeting trying to do both. Because multi-tasking is proven to be so effective. 

The production logs have nothing but 200s and everything looks good. 

Finally, the ops guy checks the S3 logs. Surprise, surprise. The image is there. Pff! Not my fault. (My inner dialogue may or may not be an eight-year-old.)

Yep, you guessed it. Another ops issue. 

Now it’s not that I think operations issues are easy. They scare the shit out of me. But I get a little huffy puffy when I’m constantly met with “it must be the code.” And I’m sure it’s beyond irritating that ops teams constantly get “it must be a server issue.” 

Which brings me to my core point: we need to work together, guys.


Change is hard. I dislike it as much as the next person. But I think this cultural shift is worth the struggle. 

By now you’ve probably heard something about DevOps. It’s all the rage these days. 

But if you aren’t an expert in what exactly the term DevOps means, here’s a quick history. 

The term was coined by Patrick Debois and Andrew Clay Shafer while attending a conference in 2008. Hilariously, Patrick had planned to speak about DevOps at the event, but received such negative feedback that he decided to skip his own session. (TIL don’t give up on ideas just because you get a poor response.)

John Allspaw and Paul Hammond joined the #devops conversation with a talk called 10+ Deploys per Day: Dev and Ops Cooperation at Flickr. The talk is 40 minutes but very much worth your time. Just play it during dinner tonight. Your kids are gonna love it. Promise.

Since then, DevOps has become a term that encompasses a company culture where developers and operations people work together. 

Traditional Thinking

Before we continue toward DevOps nirvana, it’s important to recognize your development team has a fundamentally different priority than your operations team. 

Like it not, developers are measured by the number of features they release. No CEO has ever cracked open code to review your thorough test suite or pondered at the glorious variable name you picked out. (I appreciate it, though. So you have that going for you.) 

If all of us decided to tackle our growing mound of tech debt this month instead of working on the latest and greatest idea your sales team came up with, you better believe we’d be hauled into someone’s office and chided. 

But operations people are measured on an entirely different aspect of the business: site reliability and uptime. And you better believe keeping a site up 99.999% of the time is no easy feat. 

I’ll spare you the math. That’s a little over 5 minutes downtime per year. FIVE. MINUTES. PER. YEAR.  

So, to break this down, developers must deploy new code to release new features. But deploys are the most frequent cause of downtime. 

No wonder we’re natural enemies. 

Be The Change

What we need is operations teams that think like developers and developers that think like operations people. 

It’s not easy. But it is simple. 

Operations: Empower Your Developers

Trust your team

You’re on the same side. If a developer says the code works, trust them. They’re not lying to you. And they don’t want to make your life a living hell. They honestly believe the code works. Which brings me to...

Give read-only access to all developers

To what? To EVERYTHING. I’m not saying to hand out root access like candy. But you are not the gatekeeper of information. Do you like being interrupted every 5 minutes so you can copy and paste an error message? I didn’t think so.

Developers are writing the code that runs on your systems. It’s not a reach to think they should be able to get some feedback about whether it works. After all, don’t expect developers to jump in and help when they don’t have access to your machines. 

Create consistent platforms

Integrated platforms are easier to develop and support. Pay attention to the parity between environments. Staging and production should be identical. That means the same allocated resources and the same data. Otherwise deploying will always be a roll of the dice. 

Share source control

Keep your configuration tools on GitHub with the rest of your company’s code. Code is code. It’ll be much easier for operations and developers to solve problems together if everyone knows how to locate the affected code.

Add your devs to the on-call rotation

My friend likes to say, “You build it, you support it.” No one likes to be woken up at 2:00 a.m. And if you’re tired of stumbling through the dark toward your computer in the middle of the night, share the pain. There’s no reason developers shouldn’t be on rotation. Remember, they can access logs and view your configuration tools now. Awesome!

Simplify deploys

Pushing code to production should not be a production. Unnecessary steps increase the opportunity for error and decrease the number of people who can deploy.

Oh, one more thing. Stop preventing developers from deploying their code to the QA and staging environments. Seriously. If I have to ask permission to test my shit anywhere other than dev, you deserve to put out the fire. 

Developers: Stop Being Assholes

Make operations part of the planning process

Thinking about a feature? Include operations. Talk about what will change before you write a single line of code. Discuss why this feature is important, who will need to be involved and what the risks are. You can’t deploy mystery code and then get irritated with your operations team when they start asking 100 questions. 

Make small changes. Deploy. Repeat.

If your feature requires you to change 30% of your app’s spaghetti code, break the feature into smaller pieces. Not sure if your feature is too big? Apply the same rule you use for method naming. If the method needs an “and” it’s doing too much. Small deploys make it MUCH easier to determine what went wrong in case of failure.


You know how you already included operations in your feature planning? Notify the operations team when you deploy too. Whether you use Slack or HipChat, make sure all developers and operations people have a single place to communicate. Lots of companies use an #incident channel. Find what works for you and then use it. 

Yes, and…

There’s a rule in improve that forces participants to say “yes, and…” rather than “yes, but…” Try this next time you’re in a meeting and the results will likely surprise you. That simple language change will make everyone feel heard, validated and a part of the team. 

Be open to other options

If someone on operations says there’s going to be a problem, listen to them. That means shutting your mouth and really hearing what they have to say. You’re an engineer, not God. The core competency of operations is site reliability. Let them help you. The solution you come to together will be much better than the one you thought of on your own.

Have some humility

If someone was woken up in the middle of the night because of something you released, say sorry. Buy some coffee. Help ’em out. Own your shit. When you take responsibility for a mistake, your colleagues are much less likely to make a voodoo doll of you and keep it by their bed. 

Practice Failing Together

Failure is never a question of if, but when. 

You will fail. A deploy will bring down the site. A typo in your configuration will bring users to Twitter fisticuffs. 

It happens. We’re human. And until Skynet, we’re all stuck dealing with our occasional mistakes. 

Have a healthy attitude around failure

You need to 80/20 your failure preparedness procedures. It’s okay to spend 80% of your time trying to prevent failure, but devote at least 20% to practicing how you will handle failure when it happens. We all half ignore the safety talk given at the start of every flight, but I appreciate that oxygen falls from the ceiling in the event Bane decides to crash my plane.  

Stop pointing fingers

Avoid blame. It never feels good to make a mistake. And when 20 people are required to rectify it, it feels even worse. When I screw up, I’m embarrassed. And if I feel attacked, I become defensive. I think most of you would probably say the same. Let’s give everyone a little slack. It could have been your typo. 

Leave your egos at the door

When I started powerlifting seriously, I joined a small team of intimidating lifters. The head of the group — a 60-year-old Juggernaut-like man whose traps rose to just under his ears — had one rule: leave your ego at the door. It didn’t matter that we had to strip off 400 pounds every time it was my turn to squat. All that mattered was that I listened, learned and respected the team. We could all learn a lot from that. 

Recommended Reading

Here’s a short list of books you may be interested in:

And no, I haven’t read all those books. I’m convinced people lie about how many books they’ve read. Or I watch too much Netflix. Don’t judge me.

Pushing a Node App to Heroku

If you're used to pushing Rails apps to Heroku, deploying your first Node app could be a pretty frustrating experience. 

It was for me. 

Here's some tricks to help you deploy:

  • Add Node as an engine dependency in your package.json file:
"engines": {
"node": "4.1.1"
  • Delete your node modules and reinstall all your dependencies in your production environment. To do this, run `rm -rf node_modules` and `npm install --production`
  • Double-check everything worked. Run `heroku local web` and visit http://localhost:5000 to see your app as it will appear once deployed.
  • If everything looks good, deploy as usual.
$ git add .
$ git commit -m "Added a Procfile."
$ heroku login
Enter your Heroku credentials.
$ heroku create
Creating arcane-lowlands-8408... done, stack is cedar |
Git remote heroku added
$ git push heroku master
-----> Node.js app detected
-----> Launching... done deployed to Heroku

For more information on deploying Node apps to Heroku, read through Heroku's very helpful instructions.

How Websockets Work

Oh, websockets. 

I had trouble conceptualizing websockets and understanding how information is passed from the server to the client. 

Here's the analogy I finally settled on: websockets are a lot like tossing a ball with a friend at the park. 

Websockets and Balls 

Pretend you're the server. You send out a message asking if anyone wants to play.

Your friend (the client) happens to hear and sends back another message, "Hell yea, I'd love to go to the park!"

This conversation is essentially opening the connection between two sockets. Now you're both listening to each other and can receive any messages sent. 

But here's the deal: Because you're the server, you can play ball with multiple friends at the same time. Your friend, the client, isn't so powerful. He or she can only interact with you. 

Set Up Your App

Before we dive deep into websockets, let's set up a basic Node app to work with them.

If you haven't already, make a directory and `npm init`. Work through the initial setup and then install Express: `npm i express --save`.

Within your app, create a server.js file.

This just requires some of the dependencies we'll be working with to get this app off the ground.

Create a folder called `public` and create a file within it called `index.html`. This will be the file we send to the client when they visit http://localhost:3000. Why 3000? Well, that's what we're setting on line 12 above. We're telling this app to listen on port 3000. 

In your console, type `npm start`. Then visit http://localhost:3000 and return to the console. You should see a message: "Listening on port 3000."

Cool, huh?

Ok, now we'll get to the websockets. 

We've set up the server above and now we're going to pass that variable into socketIo. (Don't worry about line 22 for now — that's for another piece of functionality.)

Now open another tab in your browser and visit http://localhost:3000. Return to your console and you should see a new message: "A user has connected. 1"

When you visited the site as a client, the server recognized the connection and notified you. 

Can you guess the difference between line 27 and line 29?

`io.sockets.emit()` sends information to ALL the sockets listening on your connection. 

`socket.emit()` sends information to one specific socket. 

How to Work with Websockets in Real Life

Now let's build some extra features to pass information back and forth between the server and the client. 

Add the following to your index.html file. 

Create a client.js file in your public folder. Then add the following.

Now experiment with your connections by opening up a bunch of tabs in your browser. See how the connection count increases and decreases as you open and close tabs.

The awesome part of websockets is that they're LIVE. You don't have to refresh the page. The connection automatically updates the information based on what's happening in real time. 

Got something to add about websockets? Feel free to comment below.

Starting a Node Project

Getting started is usually the hardest part of any project. Here's what to do. 

  • If you haven't already, download Node
  • Make a new directory and cd into it: `mkdir myapp` and `cd myapp`
  • When you enter `npm init`, you'll be given a series of options for your app. Press enter to accept the default answer or enter your own. 
name: (myapp)
version: (1.0.0)
description: An amazing app
entry point: (index.js) server.js
test command: mocha
git repository:{your username}/myapp
keywords: node, javascript, app
author: Emily Dowdle
license: (ISC) MIT
  • I'm going to use express and mocha. To install these dependencies, type `npm i express --save` and `npm i mocha --save-dev`

Turing as a great tutorial for creating and unit testing an express app. I highly recommend you follow along to get some more experience.


Video Code Along (Part 1)


Video Code Along (Part 2)

I was interrupted in the middle of recording. Here's the final part. 

Real Time Crowdsourcing App

For the next week, I'll be writing a blog series on an app I'm writing: Real Time. It's a polling app and the responses can be viewed in, you guessed it, real time!

Here's everything you need to know:

Basic Requirements and Features:


Steve is an instructor at a seven-month developer training program in Colorado. In the middle of a long rant about the merits of CoffeeScript, he wants to check for student understanding. He could pause for a moment and ask the room if they have any idea what he's talking about, but he suspects they'll just smile and nod like they always do. He knows that some students may not want to admit in front of the whole group that they neither understand what Steve is talking about, nor do they particularly care.

  • Steve decides to use Crowdsource to anonymously poll the room.
  • He goes to the site to generate a new poll and adds three potential responses: "This is old hat to me", "I have an okay understanding of this", "I have no idea what you're babbling about".
  • He gets back two links: a admin view that shows the poll and a voting page that shows the options.
  • He drops the link into Slack and his students vote.
  • Steve feels pleased when he sees that 100% of his students are absolute masters of everything he has ever taught.
  • He turns off the poll before a certain student that comes late every day arrives and messes up his perfect score.

Ember + Electron

I started this week knowing nothing about Ember or Electron. So, naturally, I set out to make an app using both.


Ember is an open-source JavaScript framework developed in 2011 by Yehuda Katz and Tom Dale. It follows the MVC (Model, View, Controller) pattern of development for developers designing scalable single-page web apps.

The Ember stack includes:

  • Ember CLI: Build tools and command line utility. Like Rails, all you have to do is run `ember new app-name` to generate a new app with a standard file and directory structure, a testing framework, dependencies (managed by bower and npm), ES6 syntax support using Babel and asset management for minifying. 
  • Ember Data: A data-persistence library. Ember Data can save records via a RESTful JSON API without any additional configuration.
  • Ember Inspector: An extension for Firefox and Chrome to help developers debug Ember apps.
  • Fastboot: An Ember CLI addon to allow developers the ability run their apps in Node.js.
  • Liquid Fire: Animation support.


Electron is an open-source framework for desktop GUI applications. Built by Github, Electron used to be known as Atom Shell and is the framework Atom was built with. 

Electron uses Node.js, Chromium (the magic behind Chrome), HTML, CSS and JavaScript to build cross-platform apps — Electron apps can run on Mac, Windows and Linux.

Apps built on Electron include Slack, VisualStudio, Avocode, PopKey and GitKraken.

You can think of Electron as a mini Chrome browser, controlled by JavaScript. It uses Chromium to display web pages as its GUI.

There are two main processes: Main and Renderer. Main creates the web pages by using `BrowserWindow` instances. Renderer is the tool used to display web pages. But, unlike normal browsers, Electron apps have access to native resources. When a `BrowserWindow` instance is destroyed, the renderer process is also stopped. 


Development Philosophy

Ember's philosophy centers around the developer. Its creators used four ideas to drive the design:

  1. Full package: Unlike most JavaScript frameworks, Ember provides the full MVC framework for ambitious client-side applications.
  2. Productivity: Ember provides tools that make getting started fast. Ember CLI provides a standard architecture with countless options for enhancement.
  3. Stability: Ember's creators and maintainers recognize the importance of backward compatibility while continuing to innovate.
  4. Flexibility: Yehuda Katz is a member of the committee responsible for creating future versions of JavaScript and made sure Ember was an early adopter of ES6.

Ember adheres to the Don't Repeat Yourself (DRY) principle and prefers convention over configuration. It is opinionated but flexible, providing developers both freedom and guidance.

Electron is best thought of by its former name: it provides a shell that wraps around your JavaScript web app to provide it lower-level access to native processes and a desktop interface. It's a fast, lightweight way to build a desktop app for any platform. 


Why Ember + Electron?

Well, for one, it seems like it hasn't been done a lot before. That's kinda cool and leaves lots of room to learn through experimentation. (Or pull out my hair in frustration, you know, either way.)

Initially I thought Electron would be the most heavily used because the app would be a desktop app. But again, it's really just a wrapper. 

Ember does most of the heavy lifting. 

And, I'm sure there are some people who would argue I should have opted for a native app and not used a cross-platform tool like Electron. 

While I think there's a time and place for that, right now, I'm focused on learning. And I love that I'll be able to deploy it for any system. 



The greatest immediate challenge was getting started. By the end of Monday, my understanding of Ember was about 15% and my knowledge of Electron was maybe 5% — and that's probably stretching it. 

I had no idea where to start. 

So, here's what I did:

(Warning, Steve Kinney makes a lot of appearances.)

After implementing edit and create on my own program, the flow of Ember started to make more sense. Ember follows an MVC philosophy, but in a very different way than Rails. I still felt unsure of how to pass information around.  


The Solution

One of the features we aimed to complete was a preview of the note (written in Markdown) in HTML, shown in a separate pane. 

So how do you get the note into another component?

Here's the code for the note:

<div class="note-content pane">

  <p class="note-content">{{model.content}}</p>

  {{textarea value=model.content}}

  {{#if model.hasDirtyAttributes}}<button {{action 'saveNote' on='click'}}>Save Note</button>{{/if}}
  <button {{action 'deleteNote' on='click'}}>Delete</button>

{{markdown-renderer note=model}}


That last line is the key: here we're passing an object to the markdown-renderer component of our app. "Model" is what it's referred to in the note template. "Note" is what it will be referred to in markdown-renderer. 

Starting to make a little more sense?

Here's the code from markdown-renderer:

{{markdown-to-html markdown=note.content}}



Pretty simple, huh? Almost too simple. One of my complaints about Ember is it seems like there's a lot of magic and I'm still finding it hard to parse out exactly what's happening under the hood. 


Next Time

I think it was overambitious of me to take on an Ember + Electron app when I'm unfamiliar with both. Next time, I'll choose one and dive deep. 

I continually struggled to figure out what docs I needed to read: Node, Ember or Electron? 

I'd like to see more apps use both technologies because I think they can work together quite well. I'm just not there yet. Maybe soon.



Interested in learning either Ember or Electron? Here's where to start.


I'm going to do something crazy

And share some really heinous code with you.

My first assignment in Turing was to build Mastermind — a simple game a user can play within his or her console. 

It seemed straightforward enough, except that I didn't know anything. I mean nothing

They hadn't even started the teaching part. We were just thrown in, like a bunch of babies at a Mommy & Me Swim Class. 

Needless to say, the original was pretty rough. 

The Refactor

I'm all for growth mindset, but I'm going to lead with the refactored version because — frankly — I'm embarrassed by the original. 

The refactor took me about two hours to complete, following the original rubric

I've been coding for three months. And I'm already horrified by my old code. What am I going to think in a year, five years, ten years?! 

The Original

Are you seriously prepared for your eyes to bleed? 

Here's my first version of Mastermind. Um, yes, that is a 130-line loop. 

def prompt(message)
  puts "=> #{message}"

def create_secret_code(code, chars)
  chars.each { |e| code << chars.shuffle.sample }

def valid_guess?(input)
  input.length == 4 && input.all? { |e| e == "B" || e == "R" || e == "G" || e == "Y" }

def fill_feedback(user_guess, secret_code, feedback)
  user_guess.each_with_index do |e, i|
    if e == secret_code[i]
      feedback[i] = e

### game
loop do

  prompt("Welcome to MASTERMIND")
  prompt("Would you like to (p)lay, read the (i)nstructions or (q)uit?")
  initial_user_choice = gets.chomp.upcase

  if initial_user_choice == "I"
    prompt("INSTRUCTIONS: The computer has created a secret code. To win, crack the code by guessing the correct colors and positions. But don't delay! The clock is ticking. Ready?")
  elsif initial_user_choice == "Q"

  loop do

    prompt("I have generated a beginner sequence with four elements made up of: (r)ed, (g)reen, (b)lue, and (y)ellow. Use (q)uit at any time to end the game.")

    code_possibilities = ["R", "G", "B", "Y"]
    secret_code = []

    create_secret_code(secret_code, code_possibilities)

    prompt("LET'S GET STARTED!")
    puts " ----- ----- ----- ----- "
    puts "|     |     |     |     |"
    puts " ----- ----- ----- ----- "

    start_time =

    feedback = ["?", "?", "?", "?"]
    correct_colors = 0
    correct_positions = 0
    num_of_guesses = 0

    loop do

      prompt("What's your guess?")
      user_guess = gets.chomp.to_s.upcase.split(//)

      if user_guess == ["Q"]
      elsif user_guess == ["C"]
        prompt("The secret code is: #{secret_code.join("")}")
        loop do
          when valid_guess?(user_guess)
          when user_guess.length < 4
            prompt("That guess is too short.")
            prompt("What's your guess?")
            user_guess = gets.chomp.upcase.split(//)
          when user_guess.length > 4
            prompt("That guess is too long.")
            prompt("What's your guess?")
            user_guess = gets.chomp.upcase.split(//)
          elsif user_guess.to_s != user_guess
            prompt("Those look like numbers. Try B, R, Y or G.")
            prompt("What's your guess?")
            user_guess = gets.chomp.upcase.split(//)

      fill_feedback(user_guess, secret_code, feedback)

      puts feedback.join

      puts " ----- ----- ----- ----- "
      puts "|  #{feedback[0]}  |  #{feedback[1]}  |  #{feedback[2]}  |  #{feedback[3]}  |"
      puts " ----- ----- ----- ----- "

      correct_colors = 0
      secret_code_clone = secret_code.clone

      user_guess.each do |e|
        location = secret_code_clone.index(e)
        if location != nil
          correct_colors += 1

      correct_positions = 0
      feedback.each do |e|
        if e != "?"
          correct_positions += 1

      num_of_guesses += 1
      guess_syntax = "guess"
        if num_of_guesses > 1
          guess_syntax = "guesses"

      if feedback.include?("?") == false
        finish_time =
        time_score = (finish_time - start_time).to_i # duration in seconds
        seconds = (time_score % 60)
        minutes = ((time_score / 60) % 60)

        unless minutes == 1
          minutes_syntax = "minutes"
        else minutes_syntax = "minute"

        unless seconds == 1
          seconds_syntax = "seconds"
        else seconds_syntax = "second"

        prompt("Congratulations! You guessed the sequence #{secret_code.join} in #{num_of_guesses} #{guess_syntax} over #{minutes} #{minutes_syntax}, #{seconds} #{seconds_syntax}.")

      prompt("#{user_guess.join} has #{correct_colors} of the correct elements with #{correct_positions} in the correct positions. You've taken #{num_of_guesses} #{guess_syntax}.")


    prompt("Would you like to (p)lay again? Or (q)uit?")
    play_again = gets.chomp.upcase
    if play_again == "Q"




Practical Approaches To Testing

Testing is a lot like going to the gym. We all know we should do it. But when the alarm clock sounds after a long night of coding, most of us hit the snooze button. 

Here’s the thing: tests should work for you, not against you. Testing may be hard, but it doesn’t have to be a nightmare. 

Mike Cohn, of Mountain Goat Software, created this testing pyramid.

He emphasizes abundant unit tests to ensure code works at a granular level, a healthy amount of integration tests to verify cross-functional behavior and a thin layer of end-to-end tests to test the user interface.

Mike’s testing pyramid matches up well with Google’s approach, which recommends your test suite be made up of 70% unit tests, 20% integration tests and 10% end-to-end tests.

Justin Searls, of Test Double, takes an entirely different approach. He recommends creating two types of tests: high-level end-to-end tests and low-level unit tests. (He also has a great talk on testing, given at RubyConf 2015.)


See all that grey space where integration tests would normally go? You can skip those. Searls believes your high-level tests will cover base expectations for user experience and ensure the app works. 

This design is sometimes referred to an hourglass pattern of testing where a test suite is made up of verbose end-to-end tests and a generous amount of unit tests with few, if any, mid-level integration tests. 

So why do some people emphasize end-to-end tests and others minimize them?

Well, there’s a bit of a debate. 

End-To-End Testing

There are a number of advantages to end-to-end tests. Managers love them because it eliminates the change of them receiving hateful twitter messages when the app breaks in production. 

And some developers — not you of course — like them because it reduces the pressure to write thorough unit tests.

But there are major challenges too. 

End-to-end tests are slow. To truly verify customer behavior, you have to spin up a browser for every test. The more code you write, the more expensive it is to run your test suite. 

Some end-to-end tests may mask issues that should have been caught in a unit test. On top of all that, some end-to-end tests are brittle to code changes. 

What’s the right answer? It depends. Small apps tend to benefit from thorough end-to-end testing while larger apps often require more dedication to unit testing. 

There’s no single “right” methodology. Your approach will depend heavily on your team and your unique circumstances. 

We’re all unicorns and snowflakes, aren’t we?