My name is Jay and I like to build things, mostly using computers, sometimes using legos or power tools. When I'm not doing work-like things, I've backpacked Europe (twice), hiked the Appalachian trail, and biked the Camino de Santiago.
I originally wrote this memo to add to the onboarding flow of a previous company.
This is a draft of the unspoken assumptions we use for team decision making and work prioritization. Its goal is to help new people joining the company to understand how, and why, we do what we do and how, and why, we make certain decisions.
Decision Making
How do we come to decisions as a team? This is how we look at it:
Speed - All things being equal, we tend to favor solutions that can be implemented faster than solutions that take longer.
Complexity - We tend to favor less complex solutions over more complex solutions. How do we define complexity? Generally, if we have two solutions that are similar, but one of them has to touch two services (let's say two new Amazon Web Services we haven't used previously) and the other solution is a script somewhere (let's say the script would live on an existing server that we're already supporting) then we would probably favor the latter solution since it has only one touch-point (a script on an existing server) over the former solution that has two touch-points (we'd have to configure two new services to make it work).
Testing - We favor easier to test solutions.
Cost - Similar to complexity, if we can find a solution that gets us most of the way there (say 80-90%) and we don't have to change much to do it, we should seriously consider that solution first over a more complex solution that fully solves the problem.
An example of this would be using DNS's round-robin support over adding a separate load balancer. Sure, the DNS solution isn't fully-featured, but it would get us 80% of the functionality of the load balancer and all we'd need to do is add some IP addresses to the DNS configuration, which has a way lower implementation and maintenance cost than adding and supporting an entirely separate load balancer.
Work Prioritization
How do we prioritize work? What criteria do we use? Generally, we try to iterate on our solutions.
For example, when we upgraded from python2 to python3, we broke the upgrade down into multiple discreet parts and then scheduled them when we had room over the course of nearly two years.
We first changed our environment to use python virtual environments instead of a global python installation so we had more isolation and flexibility with the python version we were using. Then we updated each external library to python3 compatible versions. Then we upgraded the underlying operating system to the latest version. And only after all that did we actually upgrade our python version and complete the upgrade.
The easiest way to get a big new initiative started is to break it into discreet parts and work to get each of those parts scheduled. We try and let everyone schedule pet or passion projects in between core functionality that is needed to push the business forward. Passion/pet projects are not only big new initiatives but also things like getting rid of some technical debt, upgrading dependencies, and finally fixing things that are driving people nuts.
Communication
Please don't come into a meeting and complain about how everything is wrong and it all needs to be changed, that's the surest way to get the entire team to ignore all your current, and possibly future, suggestions.
If you find that meetings are getting heated, or you're thinking things are unfair, it might be because of a definition or unspoken assumption problem. Read this entire document again and decide if the more tenured members of the team are using an unspoken assumption that you're not.
If you do have a different assumption than the rest of the team, we should talk through it and decide which assumption we should move forward with for this specific project, and maybe all future projects also.
If we're all on the same page regarding assumptions, then there might be a definition problem. Different members of the team might have different interpretations of a word's meaning. For example, we had some heated meetings about a solution because the engineer who proposed the solution had a different meaning of the word scaling than the rest of the team. This could've been cleared up by a simple definition check.
Success
We want all team members to succeed here, and not just succeed, but do their best work. We're not trying to optimize for one feature or one product, but for the entire team to to do their best work over months and years, and we've found these unspoken assumptions have helped us accomplish that goal over and over again.
These are my slides/notes from a small presentation I gave at a Walrus all-hands meeting in November 2020.
Let’s talk about forming habits...
But before we talk about forming habits, let’s define what a habit is and set some boundaries...
Changing long-term habits is rare.
Hooked (p25):
The fact is that successfully changing long-term user habits is exceptionally rare.
Habits are LIFO.
LIFO means "last in, first out." In other words, the habits you’ve most recently acquired are also the ones you’re most likely to drop first. ... The enemy of forming a new habit is your past habit because old habits die hard.
Even when we change our routines, the neural pathways remain etched in our brains, ready to be reactivated if/when we lose focus.
Habit forming time is variable.
BJ Fogg:
"there’s no research that shows 21 days is a good span to create a habit"
I had actually always heard 2 weeks, but whatever interval you’ve heard, it’s wrong and there just isn’t any evidence to back it up.
A 2010 study found that some habits can be formed in a matter of weeks while others can take more than five months (Hooked p31). The researchers, however, did find that the complexity of the behavior and how important the habit was to the person greatly affected how quickly the routine was formed.
Addictions are self destructive and are not the type of habits we are going to talk about forming.
A habit is when "not doing an action" causes a bit of pain. But the word pain is somewhat hyperbolic.
A better word might be itch. An itch is a feeling that manifests itself within the mind and causes a mild discomfort until it is satisfied. The habit-forming we want to do is to provide some sort of relief to this itch.
So now let’s talk about forming a habit in three easy steps...
Step 1. Think small.
BJ Fogg:
"To create a new habit, you must first simplify the behavior, make it tiny, even ridiculous. A good tiny behavior is easy to do, and fast"
Instead of saying you’re going to floss your teeth, say you’re going to floss 1 tooth. Instead of saying you’re going to read for an hour a day, say you’re going to open a book every day.
Step 2. Do it after something you already do.
BJ Fogg:
"put it after some act that is a solid habit for you, like brushing your teeth or eating lunch."
It's best to find an after something slot for your new habit to live in. In other words, when trying to form a new habit, figure out what it can come after.
Step 3. Repetition
Once you’ve figured out what you want to do, and slotted it into your existing routine, you can now focus on actually doing it.
BJ Fogg:
"at first, you’ll need reminders. But soon the tiny behavior will get more automatic. Keep the behavior simple until it becomes a solid habit."
The idea is if you do something small, like floss one tooth, and commit to doing it after a normal part of your routine, like after brushing your teeth, chances are you will floss more than one tooth, but you can form the habit if you are committed to at least flossing one tooth.
Sources
The main sources for all this information is Hooked: How to Build Habit-Forming Products by Nir Eyal and BJ Fogg. BJ evidently came out with a book called Tiny Habits this year, but he came and talked to 500 Startups in like 2011 about forming habits and everything I talked about today is from my notes of that event, and I’m guessing the book expands on what he talked about at the 500 Startups event, so I’m looking forward to reading the book.
Which is really our three distinct permission groupings--the dash means the group does not have that permission--so user has read and write permissions (rw-), group has read permissions (r--), and other has read permissions (r--).
We can convert those three groupings into their corresponding numeric value equivalents: rw- = 42-, r-- = 4--, and r-- = 4--.
Then, we can sum up those level values and mash the totals together: 4+2=6, 4=4, and 4=4.
So the final permission value is: 644.
And the chmod command would be:
$ chmod 644 file.ext
Example 2
rwxr-xr-x
This time, user has read, write, and execute permissions (rwx), group has read and execute permissions (r-x), and other has read and execute permissions (r--).
I'll be honest, I mainly use numeric permissions (not sure why) but symbolic permissions are super handy because they allow us to just change one grouping without touching the other groupings we're not interested in, so let's say we just wanted to add a read permission level to the other grouping, the command would be:
chmod o+r file.ext
Or if we would rather take away the read permission level from the other grouping:
chmod o-r file.ext
And that's it! An incredibly basic explanation of permissions, now that it's unleashed from the confines of my notes maybe it can help someone. Somewhere. Someday.
I've found man chmod is a great resource--if you already understand what the heck you're doing. ↩
It would actually look like -rw-r--r-- where the first dash tells us if it's a file, directory, or symlink. ↩
These are my slides/notes from a presentation for Voi Health's winter meetup at Lake Morey resort in Vermont
Narrator: "There were no slides."
They say you should start every presentation with a joke but this time the joke is on me, because no one prepared slides but me.
"How did you go bankrupt?" "Two ways. Gradually, then suddenly."
Does anyone know where this quote comes from?
Ernest Hemingway, The Sun Also Rises
I actually thought this quote was from The Great Gatsby1, I had a great picture of Leonardo DiCaprio holding up a wine glass looking smug here, so you can imagine my utter disappointment when I went to confirm the quote.
Anyway, this quote is to get us in the right mindset to talk about...
Shipping containers!
Or as I like to call them, the internet of the 50s and 60s
Simple containers show up moving coal in England by both boats and horse drawn carriages.
In 1766, James Brindley designed the box boat Starvationer with ten wooden containers, to transport coal from Worsley Delph (quarry) to Manchester by Bridgewater Canal.
In 1795, the Little Eaton Gangway opened and carried coal in horse-drawn wheeled wagons which, in form and function, were a bit like containers.
In the 19th century, shipping containers make an appearance on certain railway lines, I couldn’t find any pictures so you get a picture of my kids in front of a 19th century replica steam engine at the Golden Spike National Historic Site in the middle of nowhere Utah, instead.
By the 1830s, railroads on several continents were carrying containers that could be transferred to other modes of transport.
By the 1840s, iron boxes were in use as well as wooden boxes.
This is a Windjammer, in the 19th century most cargo is hauled using ships like this, and though the modern shipping container makes an appearance around 1917, its use is extremely limited.
The S.S. Stromboli
The use of shipping containers is so limited in fact, that this is a typical cargo ship in the mid 20th century, not a shipping container in sight.
The Ideal-X
Then came the Ideal-X in 1956, it was an old WW2 oil tanker modified by a company called SeaLand to hold shipping containers.
on April 26, 1956 the Ideal-X carried 58 containers from New Jersey to Texas, where 58 trucks were waiting to be loaded with the containers, and the modern global supply chain is born.
And here’s what a typical cargo ship looks like today.
Some stats on how fast the shipping container took over the world:
The cargo flow through one terminal at Newark jumped from 228,000 tons in 1957 to 1.1 million tons in 1959.
Registered longshoremen on the U.S. East Coast falls by over two-thirds from 1952 to 1972.
"'In 1966, commitments by ship operators and ports to containers passed the point of no return,' ... Only three ship lines were offering international container service from the United States in the spring of 1966. By June 1967, one researcher counted 60 companies offering container service to Europe, Asia, and even Latin America" (The Box ch8)
to recap...
The Ideal-X makes its maiden voyage in 1956, over 200 years from the first shipping container showing up in primitive form, and 40-ish years from the first "modern" shipping container (in 1917). In 1966 (just 10 short years later), shipping containers pass a point of no return and the entire world is changed.
So how did shipping containers take over the world? Gradually, then suddenly!
Maybe you’re asking yourself what do shipping containers have to do with Voi?
The answer is obvious, the blockchain of course (just kidding).
I like finding patterns that I can apply to my life and my job.
So what patterns can we find in shipping containers?
Sometimes, working at a startup can feel like you’re in the middle of a desert, low on water, short on supplies, all hope is lost.
And then in the very next moment everything can change.
"The scene pictured here is one of perpetual construction—building as one goes"
Sound like anything else you know?
Here’s a description of SeaLand, the company that built the Ideal-X, and ushered in the modern shipping era:
"SeaLand’s initial technology had been designed on the fly, using obsolete tanker ships, shipbuilding cranes, and containers whose length was determined by the size of the tankers, on the assumption that it could all be improved once the business was up and running." (The Box ch4)
It took over 200 years for the shipping container to break out, but when it did, the entire world changed in about 10 years.
It took First Opinion 4.5 years to become Voi, but in those 4.5 years:
We laid all the groundwork
We put good processes in place
We analyzed markets
We learned, boy did we learn
We brought everyone in this room together
McKay and I were on some introductory phone call with a bunch of you and some other people that aren't any of you, and everyone was introducing themselves, after our team finished introducing themselves I commented to McKay over chat:
"I get goosebumps when I listen to our team introduce themselves"
And I do. This team gives me goosebumps. And each one of you has helped us build this incredible team.
All this is to emphasize the thing we’ve done over the last 4.5 years:
We prepared for this moment, right now, 2018.
While studying [successful people], one thing that becomes evident is many of them put in years of intelligent, diligent, and obsessive work before they hit their inflection point.
The inflection point, if visualized, isn’t an explosion but a wave. It is the result of countless incremental improvements or breakthroughs that build up over a period of years that finally crest and accelerate the business at a rapid pace. (via)
2018 is our crest, everyone in this room is here for a reason, everything is in place, we’ve made it through the gradually part, now it’s time for the suddenly part!
I also originally thought the quote was "slowly, then all at once." ↩
These are my slides/notes from a presentation I gave at Boom Startup in Utah in July 2011. I found this while cleaning up some old stuff in Dropbox and I figured I would make it public because why not?
Hello, my name is Jay, and I work at Plancast.
Startups are a wicked problem, a wicked problem is defined in Code Complete1 as something you have to solve first to know how to solve it.
Horst Rittel and Melvin Webber defined a "wicked" problem as one that could be clearly defined only by solving it, or by solving part of it (1973). This paradox implies, essentially, that you have to "solve" the problem once in order to clearly define it and then solve it again to create a solution that works. This process has been motherhood and apple pie in software development for decades (Peters and Tripp 1976)...
One of the main differences between programs you develop in school and those you develop as a professional is that the design problems solved by school programs are rarely, if ever, wicked. Programming assignments in school are devised to move you in a beeline from beginning to end. You'd probably want to tar and feather a teacher who gave you a programming assignment, then changed the assignment as soon as you finished the design, and then changed it again just as you were about to turn in the completed program. But that very process is an everyday reality in professional programming.
The only way to know if a startup will work is to try it and find out.
Markets that do not exists cannot be analyzed: Suppliers and customers must discover them together. Not only are the market applications for disruptive technologies unknown at the time of their development, they are unknowable. The strategies and plans that managers formulate for confronting disruptive technological change, therefore, should be plans for learning and discovery rather than plans for execution. This is an important point to understand, because managers who believe they know a market's future will plan and invest very differently from those who recognize the uncertainties of a developing market."
I think it was Steve Blank who said a startup is a company formed to find a business model.
No One Size fits all...not for building your company or, on the technical side, solving scaling issues.
This is the first Plancast Techcrunch traffic spike.
Plancast's database Munin CPU graph, trying to figure out a rogue query right before SXSW.
Funny story, the rogue query was a SELECT * FROM users that was buried in a certain page load and had existed from Plancast's inception. The query wasn't a resource drain in the beginning (small user base) and so it sat there...waiting. I never noticed it because it blended in with the normal server load increases that come from a growing user base, but when we hit a critical mass of users it all of a sudden appeared, literally like flicking a switch, and became an exponential resource hog that kept taking the site down.
I said there is no one size fits all, but now I'm going to retract that a bit and say there are certain things that are pretty much universally needed across all companies.
No one cares what happened in the past. We lose half our content every. Single. Day. So there is no evergreen content benefit like other sites might have.
Normal people don't like to do things4, this has caused us to pivot to support more "professional" events.
Spend where you should, be very careful about who you hire and how you spend your money.
Developers should have nice chairs and nicer computers and monitors.
We comically over-provisioned our app and db servers, everything else we cut corners on.
He told me a story of how Larry Ellison actually got efficiencies from teams. If a team wasn't productive, he'd come every couple of weeks and say "let me help you out." What did he do? He took away another person until the team started shipping and stopped having unproductive meetings. (via)
Plancast world headquarters, circa 2011
We suck at hiring, so I've got nothing to say about that6.
Never too early to start defining your company culture.
Ours is helicopters. And I'm not a morning person.
Choose wisely! All of our downtime for the last three months has been because of MongoDB7.
Rasmus Lerdorf:
I have absolutely no problems annoying the one or two pedantic people who care about this for the benefit of thousands who don't.
Time for an old-school analogy question: Silicon Valley is to entreprenuers as blank is to actors?
I still miss this view from Plancast's office
Hoooooray for Silicon Valleywood!
I can't say where a startup should be located, I can say it is cool to be at a party and look over and see famous founder, or to hang out after some dinner event and listen to Prominent Venture Capitalist talk about raising money for his fund. There is just no other place in the world where stuff like that happens every. Single. Night8.
Any questions?
Any questions at all?
Code Complete, section 5.1, Design Is a Wicked Problem. ↩
Plancast was never larger than about four people, but the people we did hire were amazing, so maybe suck was too strong a word, it might be better phrased as we were incredibly slow to hire. ↩
Here's a fun game: guess what database Path had the most problems with? ↩
I still think this is true and still chose to leave Silicon Valley because there are many other factors that go into choosing where to live, like how good the schools are, or if you want to own your own home, or you like seasons. ↩
These are my brushed up notes for a presentation I gave during one of First Opinion's all hands meetings in October 2015.
Types of Mistakes
I'll leave it up to you, dear reader, to decide which type of mistake this is
I want to start with a discussion of the three types of mistakes, as relayed to me by a good friend of mine:
Honest mistakes - Everyone falls victim to an honest mistake sometime in their life (I fall victim to them more than most). You accidentally push incomplete code to production or you leave an ad campaign running just a little too long. We're human, these mistakes happen, so we fix them and move on.
Incompetent mistakes - The people that are committing these mistakes are either out of their league, or incompetent. Either way, it's time to cut them loose or give them a different job.
Process mistakes - Most mistakes are because of a lack of process, so I want to spend the majority of my time talking about this type of mistake and how we seek to minimize them (nobody's perfect) at First Opinion.
Minimizing Process Mistakes
In Engineering
Let's start with Engineering, since I happen to know a thing or two about how the engineering team works.
Testing
The first line of defense for engineering is our automated tests. Each major piece of our codebase has a decent amount of tests backing it up, as a completely unscientific informal look at the amount of code we have written just to test the codebase, here are some conservative stats on our three biggest software areas1:
Server - 500+ tests comprising around 15,000 lines of code.
iOS - 60+ tests comprising over 10,000 lines of code.
Web-client - Many tests comprising around 5,000 lines of code.
Other - We have multipleopensource projects that also have decent testing suites.
We are constantly adding new tests and making sure the existing tests are still relevant so our Engineers can have that sweet sweet piece of mind that comes from knowing any changes they make to the codebase don't ripple outward like silent chaos ninjas to cause unforeseen bugs elsewhere in our system because without automated tests...
Making even small changes will become increasingly difficult. Eric Evans in Domain-Driven Design: "When complexity gets out of hand, developers can no longer understand the software well enough to change or extend it easily and safely." Facebook [needs] a huge staff to keep up their momentum maintaining a big ball of mud. ... Releases will break things, because you don't understand the relationships well enough to pretend the impact of your changes. ... Next time management or clients try to convince you to move faster and throw quality under the bus, you can say sure, that will work, as long as you can hire 429 engineers to work on our iOS app. via
In fact, our iOS engineers just spent the better part of the last two weeks knocking down the last untested part of the iOS application in an effort to release the most stable app we've ever released2.
Jarid also recently spent a solid week figuring out how to automate the testing of a new feature we're working on3. I guarantee our competitors didn't devote that much time to ensuring they could automatically test that feature, that's what makes us different.
Code Reviews
But wait, there's more. The Engineers also do code reviews, these are when one engineer has another engineer look over their code and make suggestions on how it can be improved. These are incredibly valuable in helping all the engineers understand the codebases and helps the codebase stay high quality and well documented since your best critic isn't yourself, it's your colleague who has to maintain your bug infested code.
Automation
We try and automate all the things. When we deploy, we deploy with one simple command. When we add servers to our system, that's also one command. Automation means we do things the same way each and every time, thus minimizing mistakes.
When we do find mistakes, we fix the automation scripts, for a change once, fix everywhere workflow.
In Product
We have a full time user researcher that is constantly going out and talking to people about our product and planned upcoming features. Think of that for a minute, we have someone whose whole job is to go out and speak to real people about our product.
Before any major feature gets anywhere near engineering, it's gone through multiple rounds of user testing using paper wireframes, Then onto small little interactive prototypes on the phone, and only after all that is the feature ready to be passed to engineering to be built.
On the doctor side
Each of our main doctors has a staff of support doctors that help them out, our matched doctors review the conversations and monitor the quality of the interaction each one of our users has on our service.
We also have a full time QA staff that works continuously to make sure each and every interaction with a Doctor on our app is a high quality one.
Besides that, we ask the user's themselves to rate their interaction with our doctors and take that feedback incredibly seriously.
But things always go wrong
It's true, they do, and we're no exception. So when a user does have a bad interaction, we kill them with kindness, and we work with them personally to make sure their issue is resolved to their satisfaction.
Post-Mortems
"Reverse engineer your successes and turn best practices into best processes"
-Howard Lindzon
Whenever we have problems, we go through what caused the problem and talk about how we fixed it and what we are going to do different to make sure we never see the problem again, this is the best way to make sure we are always solving new problems and moving forward, instead of going insane.
In Engineering, we also change the code, add tests, or automate the problem away, the goal of these post-mortems and the implementation of the solutions is to minimize the same mistake happening a second time.
We also do one more thing...
We pass it on down
You know you want a banana...go on...go...on...
Anyone familiar with the Banana story?
Start with a cage containing five monkeys. Inside the cage, hang a banana on a string and place a set of stairs under it. Before long, a monkey will go to the stairs and start to climb towards the banana. As soon as he touches the stairs, spray all of the other monkeys with cold water. After a while, another monkey makes an attempt with the same result - all the other monkeys are sprayed with cold water. Pretty soon, when another monkey tries to climb the stairs, the other monkeys will try to prevent it.
Now, put away the cold water. Remove one monkey from the cage and replace it with a new one. The new monkey sees the banana and wants to climb the stairs. To his surprise and horror, all of the other monkeys attack him. After another attempt and attack, he knows that if he tries to climb the stairs, he will be assaulted.
Next, remove another of the original five monkeys and replace it with a new one. The newcomer goes to the stairs and is attacked. The previous newcomer takes part in the punishment with enthusiasm! Likewise, replace a third original monkey with a new one, then a fourth, then the fifth.
Every time the newest monkey takes to the stairs, he is attacked. Most of the monkeys that are beating him have no idea why they were not permitted to climb the stairs or why they are participating in the beating of the newest monkey.
After replacing all the original monkeys, none of the remaining monkeys have ever been sprayed with cold water. Nevertheless, no monkey ever again approaches the stairs to try for the banana.
Why not?
Because as far as they know that's the way it's always been done around here.
While this story usually has a negative connotation, it doesn't have to be that way, passing down good company culture is a great thing and helps makes us stronger.
We learn from each other, hopefully we retain the good stuff and fix the bad stuff.
In Conclusion
This fanatical obsession with scalable quality assurance has been going on from the very first line of code and the very first user/doctor interaction, to where we sit now at hundreds of interactions a day, to the future at thousands of interactions, and tens of thousands of interactions, and millions of interactions4. We focus so much on our internal quality because we know our internal quality eventually becomes our external quality.
This is what makes us different, this is what makes us special, and this is what will help us change healthcare.
This was incredibly unscientific, for example, in the iOS repo, I went into the testing directory and ran: find . -name "*.m" | xargs wc -l to get the line count. ↩
So we needed to add a Postgres user that could only read tables from our database--not only the currently existing tables, but any tables added in the future--and I found Postgres's permissions system surprisingly complex and unfriendly.
Sure, everything you need to know is buried throughout multiplemanual pages and tonsofStackOverflowanswers, amongother places, but I didn't feel there was anything out there that brought together all the disparate sources into one easy to follow beginner's guide to understanding Postgres permissions, so here we are, the easy guide to creating permanent read-only users in PostgreSQL1.
Before we dive in, I'm using version 9.3 of Postgres, on Ubuntu 14.04:
$ psql --version
psql (PostgreSQL) 9.3.11
So all this information will apply to that version of Postgres, and all the commands will work on that version of Ubuntu. I'm not sure what changes, if any, would need to be made for earlier, later, or different versions of Postgres and Linux.
Structure
It really helps to understand some of the different data structures that come into play when messing with Postgres's permissions, by default, every database you create has a default schema named public, the schema you use is incredibly important and could be a great source of frustration and annoyance2 as you mess with the users and try and set their permissions.
But it won't be frustrating for us because we're going to learn by example, on a fresh install of Postgres, let's create our two users, our owner foo and our read only user readonly:
$ sudo -u postgres psql -c "CREATE USER foo WITH ENCRYPTED PASSWORD '...'" -d template1
$ sudo -u postgres psql -c "CREATE USER readonly WITH ENCRYPTED PASSWORD '...'" -d template1
and let's have our new foo user own our two databases:
So now database db1 should have a bar1 table, and database db2 should have a bar2 table. Now we're ready to understand the public schema of the database.
What we are going to do is connect to db1 and list its tables, then connect to db2 and list its tables:
vagrant@vagrant:~$ psql -U foo db1
db1=> \d
List of relations
-[ RECORD 1 ]--
Schema | public
Name | bar1
Type | table
Owner | foo
db1=> \c db2
You are now connected to database "db2" as user "foo".
db2=> \d
List of relations
-[ RECORD 1 ]--
Schema | public
Name | bar2
Type | table
Owner | foo
Notice that each table belongs to schema public, but each public schema has a different table. So now we understand that when we mess with the public schema, we need to be cognizant of what database we are connected to because any changes we make to the public schema will only affect the public schema of the connected database.
The second thing we should undertand is Postgres seems to use role, group, and userinterchangeably:
The concept of roles subsumes the concepts of "users" and "groups". In PostgreSQL versions before 8.1, users and groups were distinct kinds of entities, but now there are only roles. Any role can act as a user, a group, or both.
We're only ever going to use user here, but when looking at different sources of information, you might find people using role or, less likely, group3.
The readonly user
We want the readonly user to be able to read all tables from db1. Before we do anything, let's see where we stand:
$ psql -U readonly db1
db1=> select * from bar1;
ERROR: permission denied for relation bar1
Time: 1.043 ms
One of the first things you noticed (hopefully) is our readonly user could connect to db1 without doing anything, this is because users have CONNECT privileges by default, but even though readonly could connect to db1, we couldn't look at the table because we hadn't given readonly any privileges to do so.
OK, so we better dig into the manual and learn all about the GRANT query, and since we've already spent some time learning about schemas previously, we understand each of our databases has a separate public schema, and those schemas contain our tables, so we probably want to grant some permissions to readonly in our public schema, something like:
$ sudo -u postgres psql -d db1 -c "GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO readonly"
$ sudo -u postgres psql -d db1 -c "GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly"
Let's test it out:
$ psql -U readonly db1
db1=> select * from bar1;
(No rows)
Time: 0.758 ms
db1=> \c db2
You are now connected to database "db2" as user "readonly".
db2=> select * from bar2;
ERROR: permission denied for relation bar2
Time: 0.939 ms
It looks like it worked, but just to be sure, let's double check, first, we'll try and create a new table:
Oh snap! That shouldn't have worked, what the heck? Well, let's dig into the permissions of the public schema:
$ psql -U readonly db1
db1=> \dn+
List of schemas
-[ RECORD 1 ]-----+-----------------------
Name | public
Owner | postgres
Access privileges | postgres=UC/postgres
| =UC/postgres
Description | standard public schema
What does that =UC/postgres mean? I'm glad you asked, the answer can be found, of course, in the manual:
rolename=xxxx -- privileges granted to a role
=xxxx -- privileges granted to PUBLIC
U -- USAGE
C -- CREATE
/yyyy -- role that granted this privilege
Huh, the public schema has CREATE privileges by default, well that was unexpected. So we need to remove those privileges from the readonly user, should be easy enough using a revoke query:
$ sudo -u postgres psql db1
psql (9.3.14)
Type "help" for help.
db1=# REVOKE CREATE ON SCHEMA public FROM readonly;
REVOKE
Time: 3.782 ms
db1=#
Turns out, revoking a privilege from the user directly doesn't override the granted privileges on public. Why? I don't know:
@user3669651: But it's not the same. Revoking privileges from readonly2 doesn't change the fact that every user can create tables in the public schema until you revoke from role public ... – Erwin Brandstetter Jun 20 '14 at 23:32
It really doesn't make any sense to me, but this is the reality we have to deal with, and we need our readonly user to not be able to create tables, so we have to change up the public schema:
$ sudo -u postgres psql -d db1 -c "REVOKE CREATE ON SCHEMA public FROM public"
And, of course, we'll want to give all the permissions back to our owner, because if we don't, they won't be able to create tables anymore (go ahead, ask me the fun way I discovered this):
$ sudo -u postgres psql -d db1 -c "GRANT ALL ON schema public TO foo"
Now, our readonly user shouldn't be able to create tables anymore:
$ psql -U readonly db1
db1=> CREATE TABLE bar5 (_id INT);
ERROR: permission denied for schema public
Time: 1.082 ms
Boom, we're finally making progress! I'm sure it's just a formality, but let's make sure we can read new tables also:
$ psql -U foo db1
db1=> CREATE TABLE bar5 (_id INT);
CREATE TABLE
Time: 7.252 ms
db1=> \q
$ psql -U readonly db1
db1=> select * from bar5;
ERROR: permission denied for relation bar5
Time: 0.950 ms
And we were doing so well.
We were doing so well, and then...
So now we'll need to spend some more time researching, where we'll eventually realize that granting permissions only works for things that existed the moment those permissions were granted, but if we wanted to have those permissions moving forward for all the new things, we'll need to alter the default privileges that are applied when those new things are created, so let's do that:
$ sudo -u postgres psql -d db1 -c "ALTER DEFAULT PRIVILEGES FOR USER foo IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO readonly"
$ sudo -u postgres psql -d db1 -c "ALTER DEFAULT PRIVILEGES FOR USER foo IN SCHEMA public GRANT SELECT ON TABLES TO readonly"
These are very similar to our original GRANT queries (which feels like we executed a lifetime ago now).
Let's make sure the queries worked:
$ psql -U foo db1
db1=> CREATE TABLE bar6 (_id INT);
CREATE TABLE
Time: 5.025 ms
db1=> \q
$ psql -U readonly db1
db1=> select * from bar6;
(No rows)
Time: 0.816 ms
Well what do you know, success!
it's time to party
The most important part of those ALTER DEFAULT commands is the ...FOR USER foo..., if you don't have that part they won't work (which would lead to quite a while trying to figure out why the query does nothing, not that I would know), and it's also important to run those queries for any user that is going to be creating stuff in that database, because:
$ sudo -u postgres psql db1
db1=# CREATE TABLE bar7 (_id INT);
CREATE TABLE
Time: 5.430 ms
db1=# \q
$ psql -U readonly db1
db1=> select * from bar7;
ERROR: permission denied for relation bar7
Time: 0.920 ms
db1=> \q
So you would need to run the ALTER DEFAULT queries with ...FOR USER postgres... in order for the readonly user to be able to read any tables created in db1 by the postgres user.
Well, we made it, after all that, I feel like we've really grown as people, and as friends. Hopefully this little trip into the abyss that is Postgres's permissions has been helpful :)
There is so so so much more that could be talked about, but in the interest of simplicity I've assumed you would use the default public schema and that you don't care about things like search paths. ↩
Unless you read this guide, then I've got you covered! ↩
These are my edited and annotated slides/notes from a First Opinion company all hands presentation I gave in February 2016
What do you think of when you think of constraints?
Buy a tiny house they said...you'll save so much money they said
Maybe you think of this?
I think of creativity, because constraints breed creativity.
Look no further than music...
12 notes, that's all you get! These 12 notes give us everything from Beethoven's 5th symphony to Hanson's MMMBop, and everything in between. They all use the same set of 12 notes.
If you want to you can constrain notes even more, and look at how much creativity you get, from Hit Makers:
There is a popular online video called “4 Chords,” with more than thirty million views, in which the musical comedy group the Axis of Awesome cycles through dozens of songs built on the same four chords—I–V–vi–IV. (In the key of C-major, that progression is C–G–Am–F.) This chord progression is the backbone of dozens of classics, including oldie hits (the Beatles’ “Let It Be”), karaoke pop (Journey’s “Don’t Stop Believin’”), country sing-along (John Denver’s “Take Me Home, Country Roads”), arena rock (U2’ s “With or Without You”), animated musical (The Lion King’s “Can You Feel the Love Tonight”), acoustic pop (Jason Mraz’s “I’m Yours”), reggae (Bob Marley’s “No Woman, No Cry”), and modern dance pop (Lady Gaga’s “Paparazzi”).
In 2012, Spanish researchers released a study that looked at 464,411 popular recordings around the world between 1955 and 2010 and found the difference between new hits and old hits wasn’t more complicated chord structures. Instead, it was new instrumentation bringing a fresh sound to “common harmonic progressions.” Several music critics use videos like “4 Chords” to argue that pop music is simply derivative. But this seems backward. First, if the purpose of music is to move people, and people are moved by that which is sneakily familiar, then creative people should aspire for a blend of originality and derivation. Second, it’s simply wrong to say that all I–V–vi–IV songs sound the same. “Don’t Stop Believin’” and “No Woman, No Cry” and “Paparazzi” don’t sound anything alike. These songwriters aren’t retracing each other’s steps. They’re more like clever cartographers, each given an enormous map, plotting new routes home.
Another example, LEGOS!!!!! Simple lego bricks like this...
Give us things like this...
Like the giving tree, he gave all so I could finish my yellow Lego schoolbus
and this...
I can almost hear the Full House theme in my head
And, of course, because we’re in San Francisco, I couldn't resist this last example.
Still one of the greatest movie posters of all time
Star Wars Episode 1 - The Phantom Menace. The very definition of no constraints. George Lucas had complete control, from script and casting all the way down the line to post processing.
There are no constraints on the human mind, no walls around the human spirit, no barriers to our progress except those we ourselves erect.
-Ronald Reagan
So why do we set deadlines? And pare down feature sets? And don't just hire more developers and designers and product people? Because we are, to some extent, erecting constraints to increase our creativity and productivity as a company.
In the 1980s and 1990s, Royal Farros was the vice president of development for T/ Maker, a small but influential software company. He says, "A lot of us set deadlines that we knew were impossible, enough so to qualify for one of those Parkinson's Lawcorollaries. 'The time it will take to finish a programming project is twice as long as the time you've allotted for it.' I had a strong belief that if you set a deadline for, say, six months, it would take a year. So, if you had to have something in two years, set the deadline for one year. Bonehead sandbagging, but it always worked."
I always kept a spare bowl ready but my Goldfish never did this, not once
We released our first real public version of the First Opinion app store right before Thanksgiving, which in retrospect might not have been the best idea since most of us left on vacation immediately after. McKay wanted to be very hands on with the matching in the first release. ... So when a new user signed up, McKay would get a notification, he would look over their details and decide which doctor would be right for them.
And since I was going to be on vacation, I wanted to make sure those matching notifications were rock solid, because if McKay wasn't getting notified, the user wasn't getting matched with a doctor. So I rigged the server to send an email, a text message, and a push notification for each new user that signed up.
Over the next couple of days, First Opinion steadily climbed the app store rankings, moving into the top five apps in the medical category, and McKay's phone blew up with notifications, three at a time, to the point where he couldn't get any sleep because his phone was buzzing every few minutes ... On the flip side, each of our doctors was getting inundated with tons of new users every hour, all with a question or two to ask.
During this time, we were operating under the gun, as we were working to reconcile our growth with the amount of doctors we didn't have (we launched with only a few doctors but gained thousands of users in those first couple of days).
But as we scrambled to handle the load we figured out some incredible features of our Doctor application that helped our doctors manage the load, these are features we still use to this day, but they were created while we were working under intense pressure (which is a constraint) to handle our user load with the few doctors we had at the time.
I'm Closing with this quote because I think it perfectly encapsulates why we need constraints...
He told me a story of how Larry Ellison actually got efficiencies from teams. If a team wasn't productive, he'd come every couple of weeks and say, "let me help you out." What did he do? He took away another person until the team started shipping…
Supplemental Material
A few months after I gave this presentation, on May 5, 2016, this tweet from Bill Gross came through my stream, which I think is relevant...
"Constraint actually fuels innovation. Good constraint keeps people focused." Beth Comstock, new Vice Chair of GE
The Road Runner cannot harm the Coyote except by going "meep, meep."
No outside force can harm the Coyote -- only his own ineptitude or the failure of Acme products. Trains and trucks were the exception from time to time.
The Coyote could stop anytime -- if he were not a fanatic.
No dialogue ever, except "meep, meep" and yowling in pain.
The Road Runner must stay on the road -- for no other reason than that he's a roadrunner.
All action must be confined to the natural environment of the two characters -- the southwest American desert.
All tools, weapons, or mechanical conveniences must be obtained from the Acme Corporation.
Whenever possible, make gravity the Coyote's greatest enemy.
The Coyote is always more humiliated than harmed by his failures.
The audience's sympathy must remain with the Coyote.
The Coyote is not allowed to catch or eat the Road Runner.
And while the rules might not have actually existed, the cartoons--of which there are 48 shorts, a half-hour special, and one full length movie--follow them pretty closely. 11 rules, ~450 minutes of entertainment.
Parchment
One of my coworkers sent me this article after I gave this presentation, one library decided to stop sending another library papyrus, so the other library invented parchment (emphasis mine):
One of the Ptolemies’ most drastic schemes to strike down the Library of Pergamum was the sudden cut of its trade of papyrus with the city of Pergamon. The Ptolemies hoped that if the main component of books was limited and hard to obtain, it would prevent the Library of Pergamum’s collection from growing. However, Pergamon came up with an alternative. Roman writer and scholar Marcus Terrentius Varro documented the event: “the rivalry about libraries between king Ptolemy and king Eumenes, Ptolemy stopped the export of papyrus … and so the Pergamenes invented parchment.”
While it’s not possible for Pergamon to have invented parchment since scriptures on stretched leather have been found earlier in the east, the lack of papyrus may have pushed the king to expand the use and development of leather as a writing material, Coqueugniot says. The word for parchment in Latin, “pergamīnum” literally translates to “the sheets of Pergamum,” she says.
Blaise Pascal
This feels like something about constraints but I'm not sure what, I'm including it though because why not? And the quote has a fun attribution history:
I wrote you a long letter because I didn't have time to write you a short letter
“That depends on the length of the speech,” answered the President. “If it is a ten-minute speech it takes me all of two weeks to prepare it; if it is a half-hour speech it takes me a week; if I can talk as long as I want to it requires no preparation at all. I am ready now.”
Is our Intelligence because of our Communication Constraints?
Constraints as the key to intelligence: Machine learning whiz & long-distance runner Neil Lawrence has published a research paper, Living Together: Mind and Machine Intelligence, that explores the idea that intelligence is intimately related to the constraints imposed on our ability to communicate.
...the gist of Neil’s argument is that intelligence can be distilled as a single number, which he calls an Embodiment Factor. This expresses the relationship between how much raw compute an intelligence can make use of at once, and how much it can communicate information about that computation during the same time frame. Humans are defined by being able to throw a vast amount of compute at any given problem, but then we can only communicate at a couple of words a second at most.
...The way Neil Lawrence puts it is that a computer with a 10 Gigaflop processing capacity and a communication capacity of about 1 gigabit per second has an embodiment factor of 10 (computation / communication), versus a human brain which can handle about an exaflop of compute with a communication limit of about 100 bits per second - representing an astonishing embodiment factor of 10^16. It is this significant compression which leads to many of the useful properties in our own intelligence, he suggests.
Some Tweets and Quotes Collected over the Last Few Years
I'm really into breakfast. I start almost every day with fairly brutal exercise – i'm 66 and fighting it; today that meant an hour-long run in the park. Breakfast, usually starting with yoghurt and fruit, is the reward
So one of the best things about coming home is that some seasonal fruits -- things that aren't available all year round, at least in version you'd want to eat – have arrived. Mangoes! Fresh figs!
Are these fruits better than other fruits? Objectively, no. What makes them so great now is precisely the fact that you can't get them most of the year. And that, of course, tells you that standard consumer choice theory is all wrong
The textbooks (mine included) tell you that more choice is always better. But a lot of things gain value precisely because they aren't an option most of the time. I'd probably get tired of fresh figs and mangoes if I could get them all year round.
...if you imagine that being rich enough to have anything you want, any time you want it, would make you happy, you're almost surely wrong. Limits are part of what makes life worth living.
Good design is not just easy to use but hard to misuse. The constraints are as important as the features.
And Adam Michela, answering this question from Marc Hemeon:
Do design systems prevent creativity because they lock you in to a vibe? or do they enable creativity because they help you not rethink UI and patterns and open your brain to more options.
There is a kind of magic in the idea that humans can express quasi-infinite ideas and emotions from a code consisting of twenty-six funny-looking shapes.
There's a scene in the movie Apollo 13 where the engineers at NASA pour out on the table all the parts the crew has available to them:
And one of the engineers says:
we've got two find a way to make this, fit into the hole for this, using nothing but that.
One of the psych principles I used to lecture about often in my keynotes was on boundaries as freeing, and discussed an experiment where children clustered around the sandbox in a playground with no fence, but used the entire playground area when there was a fence.
Parity isn't simply the availability of identical features - sometimes it's also the lack of the other features, for both the users and the developers working on the product.
I didn't have a lot of programming experience before starting college. I'd loved computers since my parents had brought home our Mac plus around 1985, and I spent hours in my youth configuring, tinkering, and playing1 with the steady string of computers my family owned2, but I never actually programmed anything substantial using them.
That all changed during my first semester at college, when I took the introduction to programming course. It was hard, but fun, and I managed to power through it, and I even did pretty well overall, so I figured this programming thing was going to be easy and with a few hours of work here and there I could conquer it.
Then came the second programming class. The introductory class used an integrated Windows IDE that streamlined the editing, compiling, and running of my code into one easy to use package without me having to actually understand what was going on under the hood. That second class, however, switched to Linux and required using the command line for compiling and running my code.
It was a night and day change for me. They moved really fast through the material3 because we all had previous programming experience now. I had never used Linux, I didn't know what text editor to use to write my code. I didn't even know how to compile my code using the command line, let alone run my code on the command line. Every. Freaking. Thing. Was. Different. And To top it all off, my Grandpa died early in the semester, and I missed about a week of classes.
I got behind, and I mean really behind, and it became obvious when I sat down in the Linux computer lab, the day before my first project was due, and struggled to even open a file. It was my worst nightmare. I remember feeling sick when I realized I didn't understand half the project description and that there was no way I was going to finish the project before it was due. And then I noticed the second and third projects built on that first project, so I was just going to get further and further behind. There might have even been some tears.
The next week I spent nearly every waking hour in the computer lab making nearly zero progress. And it became apparent I wasn't going to be able to figure things out on my own like I always had before. I was too crunched for time, too far out of my comfort zone, and under too much pressure. So I did something I had never done before, I went to the TA help lab and asked for help.
I'm not going to lie, it hurt my pride quite a bit to walk into that help lab and start asking the kind of questions I was asking4, especially already being a week late on the first project and about to be late on the second project. But the TAs helped me choose a text editor, design my project, and write and compile my code.
It took me multiple weeks, and lots and lots of hours in the computer lab, to completely catch up, and I spent a lot of time getting help from the TAs. But I've carried that experience with me to this day. It's the single best thing I learned while getting my Computer Science degree, and it's definitely served me better than anything else I learned, I learned how to ask for help.
2nd semester of CS, I asked "What is a server?" in my intermediate program class.
Everyone laughed, the professor looked at me incredulously & I continued with "What do you mean a computer doesn't have a screen?"
And here we are today. Ask the questions until you understand.
Dark Castle, King's Quest, Space Quest, and Police Quest, I love you all! ↩
We started with the Mac Plus, then we switched to a Pentium 386DX running Windows 3.1, only to switch back to a Macintosh Quadra 660AV right before the Powermac's came out and rendered it obsolete :( ↩
Things like module importing, namespaces, and polymorphism were completely new to me, the first course basically never required more than one file, algorithm, or data structure, and then I was all of a sudden required to have multiple files, multiple data structures in those files, and multiple algorithms to finish the assignment. ↩
As a former TA myself, questions like: How can I open a file? and How do I compile my code? are not the kind of questions you want to hear from a student that is already a week late on the first project. ↩
I was talking to a good friend and when I asked him how his job was going, he responded with, "well, I've gotten to 'I don't hate it.'" We had a good laugh and moved onto other topics, but since then I've thought a lot about his response. I spend so much time at work that it's nice to have a job I more than tolerate.
The one caveat to this is every job I've ever had--even the ones I've loved--tend to have parts that aren't much fun. But how I know I loved those jobs was because I was willing to do the bad parts because the good parts more than made up for them.
So, basically, all jobs have things you'll hate about them, but that doesn't mean you can't love them. And you'll know you love them when you are willing to do the bad parts so you can get back to doing the good parts.
If you hate your job and you're miserable getting up and going to work every day, what are you waiting for? Do it right now. Don't wait for tomorrow. Half the battle is knowing exactly what you want to do.
Or, if you can't quite bring yourself to quit (there's bills to pay, dang it!), you can at least take The Onion's advice:
I can’t stress this enough: Do what you love...in between work commitments, and family commitments, and commitments that tend to pop up and take immediate precedence over doing the thing you love. Because the bottom line is that life is short, and you owe it to yourself to spend the majority of it giving yourself wholly and completely to something you absolutely hate, and 20 minutes here and there doing what you feel you were put on this earth to do.