Does your company have crazy white boarding interviews where you’re expected to recite the curriculum of ivy league computer science programs? Your company probably has a deep existential fear of hiring someone who doesn’t know how to code.
Does your company spend most of the post-interview aghast that the candidate said he read trashy romance novels for fun? Your company is probably most afraid of hiring a bad culture fit.
What would it look like if your company was afraid of someone interviewing for the sole purpose of using your offer to get a raise?
I don’t know.
I had a weird interview experience, and this is the best explanation I have. The CTO and Director of Engineering both got really excited when I mentioned that my manager knew I was looking for a new job. I wish I had asked about it. It completely changed the tone of an interview that felt lukewarm.
Today I learned how to fix a type casting bug in our Postgresql script with regular expressions. This deserves celebration! Details below.
We have a database table that matches data to other tables based on a specific column. The values for the column are provided by the user and stored as strings. Depending on the table being matched, the values can be strings or numbers. If the user submitted bad input for a number match, accidentally including characters in their match column, the query fails.
The code handling the input was exactly the same for every match column type. I didn’t want to add a special case for this. The problem was in the query, so the fix should be in the query.
Update: I subscribe to Andy Croll’s Ruby newsletter. He suggests using \A and \Z to match the beginning and ends of a string because ^ and $ will match the ends of a line. If your string has newlines in it, it will be accepted by the regex.
Postgres has regular expression match operators. We can update our query to check that a value is a number.
The ^ operator matches the regular expression to the start of the string and the $ matches to the end. By combining them with [0-9]+ we check that every character in the string is a digit.
My brother was visiting and wanted to connect his iPad to our wifi. Because he’s in my contacts, I didn’t have to tell him my password; I just hit a button on my Mac.
It’s very inconvenient for my guests to type my super long wifi password. (All of my passwords are song lyrics with random characters.) I didn’t even know Apple had this feature, and it’s made my guests’ visits smoother.
Apple discontinued their wireless router. They absolutely should have, but it’s a shame. The Airport Express was terrible. If you used it for a Time Machine backup, everyone else’s internet connection would stall. Unplanned automatic backups would knock my roommate off of World of Worldcraft and cause Youtube videos to stutter. It was embarrassingly bad and missed the trend of having multiple access points. Eero should never have happened.
Peak iPhone is going to be a thing. Even if Apple was successful enough to sell an iPhone to every person on earth, they would hit peak iPhone. Investors don’t want stable, consistent returns. They want growth! Apple needs to grow its product line. It’s going to turn into G.E.
Ignore the Homepod, which is failing because Siri is terrible. Selling more products to your current customers is the best strategy.
Wait. What the fuck am I talking about. The Homepod might be the best product no one buys. It costs $50 more than the Bose Soundlink I bought in 2013 and has reviews comparing it to a $50,000 speaker? I… want that. Hell, I want Apple to design the power cables and other boring parts of my home appliances.
I want to live in a world where Apple is making my toaster oven.
I was recently talking to someone about Scala best practices and revisited my TicTacToe code. Here’s version two!
A bunch of the improvements were insignificant. The entire diff can be viewed here.
I thought I knew about this when I first wrote TicTacToe. If the parent object of a case class is sealed, all match statements need to include every case. If every case isn’t covered, the match will throw a compile error.
We’ll never need an instance of the parent class, so we can make the Player a trait.
The player types, X and O, don’t need to hold a value, so we should use a case object instead of a case class. Case objects are singletons, so there is only ever one of them created.
When writing a library, you want to use the most generic type possible. We use an IndexedSeq instead of a Seq because the Range type inherits from IndexedSeq. For our TicTacToe board, no extra work is done to convert our board to an IndexedSeq.
Since we’re using an IndexedSeq instead of a List, we need to change how we check if a tile is empty or not. The syntax for decomposing a Seq is messy and I always forget it. We can use the isEmpty methods to check that the board space exists and that the board space is empty.
Rename nextPlayer to currentPlayer. I always confuse next Thursday and this coming Thursday. I don’t know why I thought nextPlayer was less confusing.
The old TicTacToe would calculate the game status every time Board.gameState was called. This is the most expensive part of TicTacToe. We definitely want to calculate the new status only once when we create a new state for the board.
One benefit of checking for victory when the user makes a moves is we only have to check the row, column, and diagonals for that move. We can ignore the entire rest of the board. We can split these checks into three different methods. Compare the new way of checking for a victory to the old way. The logic is simpler and we get to keep our immutable variables!
The TicTacToe game is recursive. We want it to be tail recursive, so it will reuse the same stack frame instead of indefinitely growing the stack. Our entire game logic is wrapped in a try catch block, but only the game.move method can throw an exception. Instead of using a lowercase try, we can use an uppercase Try, specifically a scala.util.Try. By wrapping our execution in a Try, we can assign it to a variable and use a match statement. Now our Try block has a clear execution path and always calls playGame in the tail position.
Today I learned that my broken dishwasher could have shown me the error codes. I would pay extra for a dishwasher that could connect to my phone just to see those codes. I don’t want to wait a week for the repair people. I want to order the part and fix it as soon as Amazon Prime delivers.
My first gen Automatic doesn’t reliably track my location anymore, but it’s worth keeping around for its error codes. There is no confidence like walking into a repair shop already knowing what’s wrong with your car.
People love when appliances are repairable. If smart appliances told you what was wrong and how to fix the wrong, they’d be loved too.
I really hate installing Java on my personal machines, but I still want to develop Scala. Part of the Docker promise is that you develop on the same environment as your production environment. Let’s actually set that up.
Add this alias to your $HOME/.bashrc file. Reload your bashrc, and the sbt command will run the docker image in your current directory.
What if you want to serve html from a random directory? Create another alias for the Apache docker image! This works great for any code coverage libraries that output to html.
There’s too much information to consume. We need to be tactical about advice we take. Why give any of your time to someone horrible?
“But, Jim”, you say. “If the person is giving good advice it doesn’t matter whether or not they’re a bad human being.”
Do you really want to be that person’s editor, searching for the good content in the bad? Whatever good advice you find is aligned with their philosophy. If you don’t agree with their philosophy, you’re going to need to hear that same advice from multiple people before you can trust it.
For instance, why is James still in the news? Who is taking his advice?
I don’t remember where I first heard of him, but I realized his advice wasn’t for me after reading a profile on him that I can’t find. (I’ll never find the original article. His SEO game is on point) There are two stories I distinctly remember. When James’s first child was born he would wake up early and go hide in a coffee shop. I can find some proof of this story in this Mixergy transcript. James is very proud of the second story. He secured a funding letter of intent for an idea without the implementation or the ability to ever implement it. He then negotiated a deal to buy a company in that idea-space and used that company as an asset to raise money.
All of his takeaways are wrong. In both stories the true moral is lost on him.
"Kids who play video games do better as adults"  summarizes the studies that show you should let kids play as many and as much video games as they can stomach 👍 https://t.co/DUNg0pJ7K4— DHH (@dhh) February 9, 2018
I agree that that play time limits are dumb, but Penelope Trunk isn’t the best person to take advice from…
At work I need to move an application to Kubernetes, but some of its logs are necessary for user usage data and tracking ad impressions. Setting up rolling logging from our container to AWS S3 looked more complicated and risky than our current setup, so we didn’t even investigate it. Other applications at the company use Amazon AWS Kinesis. It made sense to do the same. I wrote a code snippet to push log messages to Kinesis via an Akka Stream. I could get everything working in the REPL except for the part that pushes to Kinesis.
I tried to use kinesalite, an application which implements AWS Kinesis, but there aren’t any guides for getting it up and running. I assumed you would just start kinesalite, point your Kinesis endpoint at localhost with the right port, and it would just work. I did that, and nothing happened. No error messages, no kinesalite log messages, nothing.
It took way too long (two days) to figure out how to write to kinesalite by….
Below is my example code to write to Kinesis. It creates an Akka Stream to which you can send messages. I tested each part of the Akka Stream in the Scala REPL.
Either the AWS Java SDK requires credentials (my bet) or Kinesalite requires credentials even though it doesn’t care what those credentials are. Create a $HOME/.aws/credentials file with the below contents.
This was the last step I completed to get Kinesalite working. Neither the AWS Java SDK or Kinesalite showed a single error message when trying to connect to Kinesis without authentication credentials.
You need this to setup Kinesalite.
I didn’t know you had to do this. Ops created our staging and production streams. I expected Kinesalite to accept requests for any stream, but I guess it behaves exactly like AWS Kinesis.
Run the AWS CLI tool with the following parameters. It is super sensitive to typos. I copied and pasted from examples without any noticeable spelling errors. The only messages it will give is something like “–stream-name is requires”
The first command creates your stream. The second command lists all existing streams.
In your REPL, send something to Kinesis.
Two parts to reading what has been pushed to Kinesis. First you need to find the shard iterator to read data from the stream.
Once you have the shard iterator, you can read all of the records since that iterator. Replace the –shard-iterator with the one in your output.
Your record is a base64 encoded string. The following scala snippet will decode it back to what you pushed to Kinesis.
Sending a Kinesis request for every log message is inefficient. There’s an Akka Flow called groupedWithin that lets you batch your requests by either a number of elements or a timeout. If you don’t reach the element limit within your timeout, groupedWithin will flush your batch. Even better there is groupedWeightedWithin which lets you specify a weight. Kinesis has a 1MB limit for its payloads, so we can batch our requests until we get close to 1000000 bytes.
We can’t use groupedWeightedWithin. Our application is still running on the Spray web framework. The latest Akka it supports is 2.4. The groupedWeightedWithin function wasn’t introduced until Akka 2.5.1. We’ll have to wait until we upgrade our application to Akka HTTP before we can use it.
If we kept our libraries up to date, we would have access to groupedWeightedWithin.
Written while watching tv for Phoebe’s work’s secret Santa. Recorded for next year.
Every time I upgrade Postgres major versions, I need to google for these steps. This usually happens after I’ve run brew upgrade and my database stops working. Here are the steps for future reference.
I was working on a personal project and feeling guilty about not writing enough tests. The two are different enough that no one should feel guilty for skimping on tests for their personal project.
Once a service is running in production, it never gets turned off. Decommissioning a service always ends with realizing some unknown party was using it and still needs it for their job. The standards for testing should be higher.
You’re not the only person working on your project. There’s no better way to communicate to your teammates how you expect code to behave than good tests. Having a unit test for a ticket makes it harder to reintroduce the bug.
If you’re starting a new service from scratch, testing should be a part of your project from the beginning. Pick frameworks that are easy to test. At work, we don’t use any Go router frameworks. They’re a huge pain to setup before each test.
In your personal projects, you’re the only person working on it. If you stop, it’s more likely to be abandoned than passed to another developer. It’s hard to justify tests if the project might not last a year.
Unless you’re a pro at project management and have strict requirements, your side projects are going to have higher code churn than at work. How can you justify writing acceptance tests for a web page whose layout is constantly being tweaked?
Test as much as you can, but don’t beat yourself up over it.
My dream for the internet of things is a bunch of different devices coordinating with each other. My air conditioner, humidifier, and dehumidifier should all work together to keep my apartment climate controlled and prevent me from ever having dry, cracked hands ever again. Having connected devices work together feels so far away. As we baby step towards my dream, here are some rules all internet of things devices should follow.
Washing machines and refrigerators can last ten to twenty years. Your 1985 Nintendo probably still works. Electrical components don’t degrade like mechanical components do, so the internet of things devices need to last at least as long as their mechanical counterpart.
All devices need manual controls. If the internet is out or if a storm is making the internet unreliable, the smart device should still be useable. Your Juicero doesn’t need to connect to the internet to squeeze a packet of juice.
If the wifi on my refrigerator breaks, I can’t take it into an Apple store without renting a truck. Even worse, I probably bought an LG fridge, and LG doesn’t have stores at the local mall. The smart parts of appliances need to be replaceable by the owner or be a separate module from the appliance. Also, if I detach a smart module from my refrigerator, the refrigerator should still work.
Servers store two versions of their firmware, the latest update and the previous update. When you install a firmware update, the server overwrites the older update and boots from that location.
To ensure it lasts, the devices, their api, and the hubs that control them need to be open source. Ideally all companies would open source their device software when that device reaches end of life. That’s never happened, so we need internet of things devices to be open source from the start. If you can’t recreate a device’s server features in AWS, it’s worthless.
We can’t have an app for each device. Devices need an easy to use API and support for the most common hubs.
Configuring a smart device should require pairing with the user’s computer or phone. If the device requires connecting to a service, make the user create an account or tie the account to the phone app. Unconfigured devices shouldn’t be allowed access outside the user’s home network.
Internet of things devices need to improve your life instead of measuring it. I don’t need to know how much water I drink every day. I don’t need a smart pillow or a smart bed. Not now. Not ever.
Not everything needs to be connected. Somethings can be dumb.
It’s maddening to have expensive light bulbs with wireless chips instead of expensive lamps and ceiling fans and cheap led bulbs. I get that it is easier to convince people to try out smart light bulbs when they don’t need to rewire their home, but the market has been proven. People love programmable light bulbs that can change color. Light bulbs are going to burn out. They shouldn’t be expensive. They shouldn’t be wireless.
We don’t need more wireless things. All of our wall outlets are going to be USB-C someday. Someone needs to start building the products that take advantage of our fast, low powered, wired future.
The power is out. I want to write a blog post, but the only thing worth talking about this week is Susan J. Fowler’s story about her awful year at Uber.
My friends will share her story commenting that “if this is true, it’s damning.”
I’m convinced it’s true.
Even at our favorite companies senior managers will harass female employees and those companies’ employees eventually find out that HR exists to protect the company from liability and not to help them.
Hiring people with a strong sense of entitlement very rarely works out, no matter how good they are on other axes— Sam Altman (@sama) November 30, 2016
The absolute opposites of an entitled employee are the apprentices in Jiro Dreams of Sushi. The apprentices have to work for ten years making rice and doing other menial tasks before they’re allowed to make a dish. Jiro’s desire for perfection really shows his dedication to his craft.
The film paints a portrait of an exacting patriarch who demands perfection from himself, his sons, and the hard-working apprentices who work up to 10 years before being allowed to cook eggs.— Influence Film Forum
Although… why would anyone put up with that? If you were even a mediocre chef, why would you toil under Jiro instead of creating your own restaurant or being a more senior chef at another restaurant? Only two types of employees will apprentice with Jiro, the sushi fanatics and the people who can’t find a job anywhere else.
Sam Altman wants employees who have no where else to go.
Scala finally clicked. It took a year of writing production code with it and completing Coursera’s Functional Programming Principles in Scala course (lucky me, it’s taught by the creator of Scala, Martin Odersky). When I got to the programming exercises in the Scala chapter of Seven Languages in Seven Weeks, I wanted my code to follow every Scala principle. I wanted to only use immutable variables and use functional programming components instead of for loops.
Here’s my tic tac toe code in its entirety. You can run it using Scala’s REPL. I didn’t want to spend an entire weekend optimizing it, so this is the first complete version. I’ll tell you why it sucks at the later.
I’m pretty proud of it. It highlights the best parts of Scala which are the case classes, match statements, and decomposing an object in a match statement.
Case classes are like fancy, extendable enums. They can inherit from other classes, override default methods like toString, and have new methods. We can create a fake-enum type by having our case classes inherit from a Player base class.
Case classes can have constructor arguments. This lets us associate a value with the enum.
Match statements are like fancy switch statements. They evaluate in order and can include additional logic. In Scala the underscore is the we don’t care character. In a switch statement it will always evaluate. Because it’s always evaluated, it should always be your last case match.
Match statements can also decompose an object into variables for you if it matches a specific format.
In our move method, we use a match statement to split a List into its head and its tail. Outside of a match statement the line head :: tail is appending head to the front of the List tail using the :: operator. Match is checking if your variable could be deconstructed to match this pattern.
We check that the head, the Tile at the specified location, is empty. Then we insert a new tile. Any operator beginning with a colon is right associative. head :: tail will insert head at the front of the List tail.
Below is the same code refactored to show which variable is calling the method.
Maybe our board should’ve been a Vector of Tiles instead of a List. Lists in Scala are linked lists. Linked lists are great if we want to have immutable collections, but it’s kind of hard to think about when it’s new to you. Vectors would’ve let us access tiles by their index and simplify code.
For example, we need to iterate through the List to the tile’s location to check if a it is empty or not. This is only optimal if the user’s input is valid. If the input is invalid, we have to iterate through the list multiple times. If we used a Vector, we could create a separate method to check if a move is valid instead of trying to save operations by shoving the check into our move method.
Using a Vector to check if a move is valid would fix another problem. Our game loop is recursive. Because we catch invalid input for the moves in a try catch block, it’s not tail recursive. When we call playGame, the code in the catch block could still execute. Tic tac toe has at most nine moves. If our game was more complex, we could actually cause a stack overflow.
Overall, I think using immutable values made things more complex. I spent most of my time writing the gameState method. I wanted to use Lists and foldLeft to build the results. If I allowed myself to use mutable values, I would have just used a for loop to check each of the rows, columns, and diagonals for a winner.
…you should read “Developer hiring and the market for lemons” by Dan Luu about the four dumb excuses companies give for not being able to find good employees. It’ll make you feel better about how arbitrary it all is.
One of the biggest threats to your startup are junior people who bring the germ of big company bogusness.— Paul Graham (@paulg) March 6, 2016
Some things shouldn’t be tweeted. Opinions with nuance need complete thoughts. We can assume Paul Graham meant something very specific that we can’t intuit from his tweets.
But we’re not going to do that. Because fuck Paul Graham.
Here are some things big companies are better at than startups.
HP was my first job out of college, and I worked there from 2008 to 2012. It was huge, corporate, and everything a sane person should hate in a company, but I also learned a fuck ton.
Every year at HP, we had to watch an hour long video on ergonomics and take a stupid test to prove that it took. If you didn’t complete the video, your manager would get an email about it. Then his manager. I didn’t let it get any further, but it would have trickled up to the CEO.
At my company, our interns have a week of learning about our programming languages, build tools, and culture. If ergonomics is shoved in there, it doesn’t take. Their posture is horrible.
My coworker complained about back problems. Instead of ergonomics training like HP would force upon you, our supervisor agreed to let them work from home mornings (my company really discourages working remotely), and it didn’t help. They never learned how to setup her desk.
There has to be a balance between boring videos and never teaching anything. Maybe IT could give employees a pamphlet when they set up their desk.
The biggest difference between junior developers and developers is being able to split a big task into smaller tasks. The younger you are the less you know about how long something should take to complete.
On a Rails app, I’ve seen someone try to switch the UI from Erb templates to Ember, update the models and their relationships because the old structure didn’t make sense with the UI, change the UI to match a new mockup from our UX designer, and move to Active Model Serializers all at the same time. After three months, they had a gorgeous new UI that was still unusable. They did good work, but it was never merged because it was always incomplete. They needed someone to direct them, and not let them bite off more than they could chew. Their failure was our fault.
At big companies you have a dedicated project manager and are forced to split your tasks into measurable chunks. When I was a consultant, our estimates for each ticket were measured in hours. No ticket would take more than 24 hours or it would be split up into smaller tickets. Junior developers need that guidance until they learn how to do it naturally.
Maybe I was lucky, but my manager at HP knew what he was doing. Eden saw his job as facilitating my development and getting out of my way. If something was critical, once a day he would stop by my desk and ask if I needed anything. He excelled at leaning on other teams to do their job when we were waiting on them.
At the startup where I work, communication between teams that don’t regularly work together is still… hazy. I’m working on ads. Another team is working on a new project. My manager proactively sends me to reach out to them about their ad needs. They don’t need us. The first ads will be native. A month later our team, discussing with the ad trafficking team, decides to serve native ads through Google DFP to track clicks and impressions. We release the first native ads on our site. Now when that other team starts working on ads, they’ll circle back to us. The roles aren’t defined, so there’s misdirection and wasted time.
I work on the ad rendering. What happens whenever anyone ever wants to make a change to something that sorta, kinda touches our code? We’ll give them a 30 minute overview of how our code works and… they’ll realize that we’re not the team they need to talk to.
This is a fun conversation that you can in the world of startups.
Tester: “Your branch is producing some weird logs. When are these events supposed to fire?”
Staff Engineer: “I don’t know. How does it work in master.”
Three testers start regression testing, comparing the two branches
Let me suggest that the biggest threat to startups is out of touch VCs who convince you to ignore a huge market of skilled developers.
Paul Graham is responding to clarify that it's junior developers that are a problem and senior developers and the hiring managers know the risks and what they're getting into. That... doesn't... make... sense. Junior developers are way more adaptable than older developers.
Holy fucking shit. This is propaganda. When I think of a big company, I think of HP and IBM. Not Paul Graham. Paul Graham is shaming college kids to keep them from joining Facebook, Google, and Apple. There is a shortage of qualified developers, and Paul Graham is trying to keep Y-Combinator founders and future employees of Y-Combinator companies from joining big companies. Fuck you, Paul Graham.
Hey I fixed that new hot meme ya'll are sharing pic.twitter.com/2eZvANvK7p— Make Fyles (@thealphanerd) March 7, 2016
Blake Ross, Firefox wunderkind, has the best analogy for the Apple versus the FBI debate.
There is a slim chance that there is data on a terrorist’s work phone, and the FBI wants Apple to allow a backdoor key.
An airline pilot locks the other pilot out so he can crash the plane into a mountain, and the airlines don’t add a backdoor key to the cockpit door.
They understand that there are too many bad people trying to get in, and it’s so rare that a good person is trying to get in that it’s not worth risking security with a backdoor key.
The security we encounter every day — when it works at all — is usually built out of shades of gray: Lock your door. Need more? Arm your alarm. Even more? Don’t feed Fido for a day. Marginal benefits, marginal costs.
It’s easy to assume that digital security is just another spectrum, and politicians love to reinforce that — gray’s their favorite color. Every presidential candidate is offering the same Michael Scott solution: Let’s preserve everyone’s security at once! Give a little here, take a little there, half-pregnancies for all.
Unfortunately it’s not that complicated, which means it’s not that simple. Unbreakable phones are coming. We’ll have to decide who controls the cockpit: The captain? Or the cabin? Either choice has problems, but — I’m sorry, Aunt Congress — you crash if you pick 2.— Blake Ross
A project usually has a single gamble only. Doing something you’ve never done before or has high risk/reward is a gamble. Picking a new programming language is a gamble. Using a new framework is a gamble. Using some new way to deploy the application is a gamble. Control for risk by knowing where you have gambled and what is the stable part of the software. Be prepared to re-roll (mulligan) should the gamble come out unfavorably.— Jesper L. Anderson on Medium
Jesper, who freaking tests distributed databases and other systems, has much more specific advice on how you should build your apps. He has good advice. Read it. (I promise not to make you write Erlang)
I really want to set up a WordPress blog, but I don’t like php, I hate having to configure WordPress, and I don’t want to worry about security problems. You should choose the programming languages and tools that fit your personality. Both Ruby on Rails and Ember JS have friendly communities and good documentation. Rust has a great community too, but my Rust is rusty.
Most programming tutorials stop once you have a working prototype. You never end up learning how to properly use a language or framework unless you’re using it at work. This guide is going to follow my entire process from coding to deployment. I’m going to assume you’ve seen some Rails tutorials but also that you’re starting on Rails 5 from scratch.
Our WordPress clone, which I’m going to call PaperbackWriter, will be a Rails app that serves cached Rails views. I hope it’s as fast and cheap to serve Rails views as static HTML pages. If they’re not, we’ll figure it out. Because HTML forms are awful, even using Rails, we will make an Admin section using EmberJS. We’ll need to build authentication and a process for password resets. I don’t know what I want to do about deployment. I’d like PaperbackWriter to live on a cheap 1 processor, 512GB RAM Linux install in the cloud, but I also want it to be able to kick WPEngine in the teeth.
The first thing we need to do is install Ruby. Mac OS X ships with too old of a version of Ruby to support the latest Rails. At the time of writing most recent Ruby is version 2.3.0. You can download it yourself, but I’d rather use a Ruby manager. I like compiling my own Ruby and installing it to /opt/rubies/. I HATE having Ruby installed in my home directory. I back up my home directory. I don’t want to waste space on a Rails install. Chruby does everything I want, and their documentation is amazing. Follow their instructions and install Ruby 2.3.0. If you’re reading this from the future! install whichever Ruby version is the newest.
Since Ruby on Rails 5 will be released by the time this series finishes, we’re going to use the latest Rails 5 beta. I’m sorry if you’re following along on Windows. I know it has some issues.
First we need to check on Ruby gems what the latest version of Rails 5 is available. In the present, it is still 5 beta2!.
Install rails by running the command below. We’ll also need the bundler gem.
With rails 5 installed, let’s create a new Rails app named paperbackwriter.
Adding the -B flag prevents bundle install from being run. We’re going to make some changes to the Gemfile before we install the Rails dependencies.
Postgres is the best open source database not owned by Oracle. Since we’re planning to deploy this to a server, we should start working with postgresql now.
By default Ruby on Rails will use sqlite as your database. That works fine for testing and some production apps, but we want to develop our app the same way it will run in production. This improves our chances of catching bugs before we deploy. Another reason is your laptop/desktop is way more powerful than the tiny virtual server on which we’re planning to run our app. If you write a database query that’s slow on your development machine, it’ll be dead slow in production.
Homebrew will display instructions for starting Postgres. I prefer to create a symbolic link from Postgres’s property list file to the LaunchAgents directory. This will start Postgres when our machine boots. I’ve spent way too much time trying to figure out why an app wasn’t working when the database wasn’t running yet. Since the database will only be used when our app is running, it should not hog our machine’s resources.
The default Rails Gemfile in the top level directory should look something like this.
If we remove the gems we’re not going to use yet, our Gemfile gets a lot thinner. Update your Gemfile to match mine.
I removed the version from the pg gem. When starting a new project, I prefer to use the latest version of libraries. Bundler will always install the version of a gem listed in the Gemfile.lock file. If we ever need to upgrade a gem, we can upgrade it by itself without changing the versions of the other gems.
We want to specify our version of ruby in the Gemfile. Every once in a midnight dream, the wrong version of ruby is loaded. We want bundler to fail if there’s a version mismatch. Add the following line to the top of your Gemfile.
My company subscribes to The Twelve Factor App. The Twelve Factor App methods make it super easy to deploy apps. We’re going to follow their advice and store all of our configuration options as environment variables. For development, all of our config will be saved to a .env file. The dotenv gem will automatically load a .env file if it finds one in the root directory. Add the dotenv gem to the top of your Gemfile. Other gems will require your environment variables exist, so dotenv needs to be loaded before they are.
The last gem we need to add will be our JSON serializer. The best serializer for talking to EmberJS is the Active Model Serializer gem. The version of Active Model Serializer that best supports JSON API is still in release candidate, so we need to include a version number when we add this gem. Usually using pre-release builds is bad. The problem is that the next version of Active Model Serializer isn’t based off of the previous version, 0.9. The next release is branched off of version 0.8. It will be easier to risk using the release candidate than it would be to use the most recently released stable version and change our code later.
Your finished Gemfile should look like this.
Sometimes bundler will install gems to your home directory. I hate installing to my home directory. Run the following command to configure bundler to install in vendor/bundle directory inside your app.
Now run bundle install
Now we need to configure our Rails app to use environment variables for all of its configuration.
First we’ll update config/database.yml. We want development, test, and production to share the same basic settings. We set those in a default collection. Our development, test, and production collections will inherit from it. We want our database name to be different but similar for our development and test databases. When running tests locally, we don’t want them to overwrite or delete our development data. Change the yaml code so that it reads the database name from an environment variable called DATABASE_NAME and appends _development or _test to the end of the name.
The production database will be more complicated. The production database will need more configuration options passed to it, such as the user name, password, or connection pool size. Instead of getting the database name from an environment variable, we want to get a url to the database. We’ll go over the exact value of the url when we deploy.
The finished database.yml file will look like the one below.
The only other config we need to update right now is in config/secrets.yml. The secret_key_base is used to encrypt your cookies. We want to keep it secret and out of our source control. Set the secret key to be an environment variable like below.
Notice how we use ENV to access the database environment variables and ENV.fetch to access the secret key base? ENV.fetch will throw an error if the value isn’t found. If our database environment variables are missing our rails app won’t be able to connect to the database. It will be obvious where the error is occurring. If the secret key base isn’t set, our cookies won’t be encrypted. If it’s missing we want to know immediately, so we use the ENV command that throws an error if the value is missing.
Now try running your database migrations with the command bundle exec rake db:migrate.
You should see an error similar to “rake aborted! KeyError: key not found: “SECRET_KEY_BASE””. Our environment variables aren’t declared yet. The dotenv-rails gem expects a .env file in the top level of our project. Create that file and add our environment variables and their values.
We can generate a new value for the secret key base by running rake secret. The secret will be a very very long hexadecimal string.
The finished .env file should match the one below.
Rerun your migrations, and they should create a new development database. If you see any error messages verify that Postgres is running on your machine. If you get an error message about your database not existing run rake db:create first.
Now we’ve done everything we need to run our rails app. Run bundle exec rails s and visit http://localhost:3000.
You should be viewing the Rails start page. It’s pretty but boring. Next, we’ll add some data.
When I picture our blog, the main page lists articles chronologically. They are paged or infinitely scrolling. It doesn’t matter which. You should be able to read every single article by clicking the same button. Each piece should be tagged. We should be able to click on any tag and see a chronological list of all posts with that tag. We want our app to create single page and multiple page content. My idea for this is to have a model called Writing which corresponds to a post. Then each post has multiple Section models. Then if a post has more than one section it should be paginated.
Let’s start with tags and writings. Generate Tag and Writing models with the following console commands. In the commands, the g is short for generate. Generating a scaffold will create a model, controller, and the default views for index, show, edit, and new. Other options for generators include generating resources and individual models, controllers, and views.
Each Writing will have many tags and each Tag will have many Writings. They won’t be dependent on each other. This is a good time to use a has many and belongs to many relationship. Run the command to generate a join table migration.
Open the migration and change it to match the below code. Rails is pretty good at guessing what you want. You should just have to uncomment the two indexes in the file.
If you look inside the config/routes.rb file, Rails automatically added resources
Open app/models/tag.rb to look at Tag model. Rails makes it super easy to add validations to your models. Add a validation to the tags to force them to have unique names.
Add the same validation to app/models/writing.rb for the title field.
Now let’s add the Section model. Sections are going to be the pages to our posts. They’re going to need a title, which will be optional, and they’re going to need the text for the post. Adding writing:references to the end will create a beongs_to relationship. Rails generators are pretty awesome.
Reciprocate the relationship by adding has_many :sections to app/models/writing.rb
We will never want a section without the writing associated with it. Update the routes to add sections as a subroute of writings
Sections won’t ever exist without a Writing. Update the create method on the sections controller to always look for a Writing id and associate the section with it.
We just want rails to act as an API server right now. It will talk to our Ember admin page to create new writings and sections. The views for writings, tags, and sections exist now, but we’re going to remove them. We’ll add the views later.
Open the controllers for writings. The index view is creating a class variable for all writings. It looks like @writings = Writing.all. At the end of the index and show methods add code to render @writings as json. It will look like this.
Do the same for Tags and Sections
Everything should be created and working. Let’s seed our database with test data so we can verify the routes work. Open seeds.rb and add the following code. This will create several tags, one writing, and three sections to that writing.
Run bundle exec rake db:seed to execute your seed.rb file. This will fill the database with the data we just specified.
If your app isn’t already running, run bundle exec rails s
Visit http://localhost:3000/writings and you can see the writings object we created. Also checkout http://localhost:3000/tags. Sections are trickier. We added them as a sub route of writings. Visit http://localhost:3000/writings/1/sections to view the sections we created for the writing with id 1.
In the next post we will add an admin screen using Ember.
If you haven’t committed your code to a git repository now is a good time.
But the dropping posts on Facebook prove that everybody is not a star. That the wild west atmosphere of the internet is a phase. And as things solidify, cyberspace resembles the rest of society… There’s a very thin layer of superstars/winners, a small class who believe the low barrier to entry means they can win the lottery, while the rest have checked out and are interacting one to one and consuming when they’re not.— Bob Lefsetz
Most startups fail. You’re not going to get rich. You’re not going to get famous. Make sure you get paid and make it’s worth it for you.
When I graduated college I thought getting paid was making the next Facebook, but my first job paid me $62k, enough to travel overseas once a year and have substantial savings left over. I could’ve done better if I kept looking, but interviewing sucks. $62k is just right for someone straight out of college in 2008. Starting salaries in Texas are closer to $70k today.
Then I met someone with a master’s degree making $55k at a consulting company because “That’s what we can bill for you”. They shouldn’t have let you accept that job. They know you’re going to leave for a better paying job as soon as you have a year’s experience. They just want to take advantage of your low salary and standard bill rate for as long as possible. If you’re starting that undervalued at a company, you’ll never catch up to your market rate with only the standard 5% raise every year.
Even worse is if you work in IT. Even the lowliest programmer makes more than you. If you’re making websites in your free time and deploying them to a server, you’re qualified enough for a junior developer position, and you’d be shocked at programmer’s starting salaries.
Talk to your friends about salary. Talk to your students about salary. Talk to your family about salary. Friends don’t let friends get paid less than market rate.
We're going to ignore resumes, b/c we've found they have little predictive power, mostly serve to feed ego of people doing resume review.— Patrick McKenzie (@patio11) October 31, 2015
A long time ago, I had a manager who read way too much into resumes. When I was interviewing, he told me that he loved to start at the back of the resume and work forward to where the applicant was now. He wanted to make sure all jobs the applicant chose were congruous. Fucking prick.
subscribe via RSS