Resume Advice

Gergely Orosz has a nice Twitter thread:

“After reviewing 200+ dev resumes from software engineers who are job hunting - new grads, seniors, principals - here are the 9 most common pieces of feedback I’ve given.”

  1. Too many personal details on the resume. Many devs have their photo, birth date - some even marital status, number of kids.
    • DONT DO THIS. You are exposing yourself for biases, have nothing to gain & much to lose. These details are not required by tech companies.
  2. Not having the things easy to find on the first page that hiring managers and recruiters look for. Relevant languages & technologies, work experience (titles, companies), and dates to give an idea of the number of years’ experience you have.
  3. Talking about the “what” you did, but not the results, impact and contribution, with specifics. Too many resumes list “Company X, worked on System X”. You want to convey the impact your work had, and how you (significantly) moved the needle.
  4. Being verbose about things hiring managers don’t care about. This is especially for experienced candidates. I don’t care about the Symbian project you did 15 years ago, or the 3 internships you did 10 years ago. Focus on the recent impact, achievements, and skills.
  5. “The basics” missing. Spelling mistakes and poor grammar convey poor attention to detail. Hard to read formats, BOLDING every SECOND word, date formats flipped, compared to the country you are applying to.
  6. “Abusing” the one-page resume. This is especially for ambitious new grads. I’ve seen tiny fonts, so that two pages of projects with intricate detail could be listed on that one page. Be concise and focus on the most important things. Project #6 is not that important.
  7. Having the exact same resume for all positions. In the day of ATSes (Appliaction Tracking Systems), you NEED to customize your resume for the position, especially when you will be up against hundreds of them. Tailor and cut down your “master” resume for that job listing.
  8. Putting your weaknesses front and center. A backend-heavy fullstack dev attached links to the terrible UI of the app they built. Another dev listed all their “novice” languages. Your resume should sell you - you can talk about weaknesses on interviews. This is not the place.
  9. Links on the resume pointing to poor work. Github links pointing to a repo with no decent projects - not even a readme. A link to Medium bringing the first post about on how this person hates their team. You don’t have to add links. But if you add them, make them count.

Code Patterns for API Authorization: Designing for Security

I’ve be wanting to write basically the same blog post for awhile, but damnit, Tanner Prynn beat me to it, and he did a great job.

Tanner describes four different common patterns when implementing authorization for web apps and APIs, and compares their security trade-offs.

#1: Ad-hoc: each route defines its own permission checks and logic.

def get_message(context, message_id):
 message = get_message(message_id)

 if context.user == message.recipient:
   return message

 return "Not authorized"

Authn is performed as a one-off, direct object ID comparison within the controller method, which means this is very hard to audit at scale. Once you get to a certain size/complexity of app, basically no one fully knows what access controls should be. “Ha!” you scoff, stroking your distinguished security greybeard, “Surely every dev team should know what their product’s access controls should be.” Well, in a number of pen tests I performed, for very complicated apps (e.g. you have users, admins, teams, orgs, the teams/orgs can have relationships, …), that’s actually sometimes not the case.

Testing these apps can best be done via dynamic testing, for example with Burp Suite extensions like AutoRepeater or Autorize.

#2: Route-based: each route explicitly declares what permissions it requires, generally by a decorator or in a routing file, and middleware checks the authz before running the route logic

def get_message(message_id):
  return get_message(message_id)

Pro: authz checks are very clear.


  • Sometimes cannot effectively model the authz you want. For example, there are often cases where you need more context to make the decision (you want to check whether a user has access to a specific object), so you implement that check again in the route itself.
  • Sometimes you may fail open (no annotation => no authz checked)

On democracy

One meta concern I have is about the future of American democracy.

  • Americans continue to become more polarized, and there’s no central leadership trying to unify and heal us.
  • Trump has been systematically firing everyone who disagrees with or investigates him (e.g. inspector generals).
  • U.S. police have attacked journalists more than 120 times since May 28: “in the majority of the cases we have recorded the journalists are clearly identifiable as press, and it is clear that they are being deliberately targeted.”
  • Governments often use times of unrest to gain additional powers to “keep the peace.” Does Trump seem like someone who willingly gives up power once he has it?
  • It’s unclear if Trump will step down if he doesn’t win the election. TODO michael cohen link

Last year I visited Yad Vashem, the Holocaust museum in Jerusalem. It was one of those most moving experiences I’ve ever had, bar none. It was incredible to see what humans can do to each other, and justify to themselves.

One of the biggest things that stuck out to me was how fast everything happened.

It seems obvious and inevitable when we read about it in books or see exhibits in a museum, but what happened and where things were headed was not obvious from the inside at the time.

From Hitler’s rise to power, to his initial concerning rhetoric, to individuals vandalizing synagogues and Jewish owned businesses, to official government policy to round up undesirables - that all happened in few months.

One Jewish man was later asked, “Why don’t you leave when hooligans started throwing rocks through your business’s windows and harrassing you?” He replied, “Because we’re German, we’ve lived here our whole lives. Yeah, there was some harrassment, but that’s just the way it is being a Jew. And yes, there was some concerning rhetoric, but we thought those were just words to rally people.”

Things can change quickly.

Do I think we’re in the same situation? No, but I would caution us to be very careful when checks and balances are eroded, and power is consolidated.

We take for granted that America is the land of the free, but that’s not a guaranteed future. We fought a war to make create America that way, hopefully we won’t have to fight another.

Work Is Work

H/T Jon Hawes for the link to this.

If agile, flat organizations, code reviews, monorepos, open offices, fancy type systems, etc. were actually the causal factors they’re purported to be, then why do so many organizations adopt those practices without success?

If you squint hard enough, an organization doing work is just an incredibly complex, dynamic, distributed, parallel process.

Modeling organizations as parallel processes can inform the way we design them.

The work capacity of an organization scales, at most, linearly as new members are added.

As an organization hires more employees, work on productivity improvements must be a constant priority. Internal tooling, training, and services must be developed and fielded to ensure that all members are able to work on problems of continuously increasing impact. The ceaseless pursuit of force multipliers is the only possible route to superlinear productivity improvements as an organization grows.

It must be emphasized that this linear bound on work capacity is a ceiling, not a floor. One cannot do better than linear, but one can certainly do worse. There are many other factors which act as a drag on work capacity, and organization-wide improvements in productivity are critical in mitigating them.

Contention costs grow superlinearly as new members are added. Parallel solutions to tasks are rarely perfectly concurrent, and often require some sequential critical sections.

Shared resources aren’t necessarily physical things, like bathrooms or printers; they can be digital, like files in a source code repository or tickets in a bug tracker, or organizational, like code reviews or work assignments. As with writing highly-concurrent applications, building high-performing organizations requires a careful and continuous search for shared resources, and developing explicit strategies for mitigating their impact on performance.

As with heavily layered applications, the more distance between those designing the organization and the work being done, the greater the risk of unmanaged points of contention. Top-down organizational methods can lead to subdivisions which seem like parallel efforts when listed on a slide but which are, in actuality, highly interdependent and interlocking. Staffing highly sequential efforts as if they were entirely parallel leads to catastrophe.

Coherence costs grow quadratically as new members are added. Working on complex tasks using parallel resources (or with a group of people) requires communication.

Communication takes time. If the relative percentage of people who need to talk to each other to get something done stays constant as the organization grows, the total time spent communicating will grow quadratically as the work capacity of the organization grows linearly.

We can consider group meetings as a batching strategy to reduce the number of entities involved in point-to-point communications, but the effectiveness of this strategy depends heavily on the relative overlap of groups and the group structures.

The only scalable strategy for containing coherence costs is to limit the number of people an individual needs to talk to in order to do their job to a constant factor.

If the organization’s intent is to increase value delivery by hiring more people, work efforts must be as independent as possible. Leaders should develop practices and processes to ensure that the work efforts which their strategies consider parallel are actually parallel. Shared resources should be continuously managed for contention, and where possible, the resources a group needs should be colocated with that group (e.g., if the work involves a lot of design, staff a designer to that group).

If an organization is largely working on the same types of problems it was in previous years, it’s cause for concern. Teams dedicated to internal tooling should be staffed and given the explicit direction of building tools and optimizing processes to help increase their co-workers’ productivity. Go long on high-leverage tools, but stay grounded in whether or not they actually help.

  1. Keep the work parallel, the groups small, and the resources local.
  2. Prioritize the development of force multipliers.
  3. If possible, factor work products into independent modules; if not, grow slowly and optimize.

If your work product–e.g. codebase, documents, etc.–can be factored into independent modules, do so. The key word there is independent. Slicing your shit up into a hundred microservices will not help you if everyone needs to change ten of them to get anything done. Some problems are not particularly amenable to partition; these are also problems which do not benefit much from additional workers.

Organization leaders should keep the development of a product portfolio as an explicit goal. Feature or product ideas which are complimentary to the organization’s overall business strategy but don’t naturally coexist with the main product can be developed as separate products by independent teams.

  1. Keep responsibility assignment matrices small, sparse, and local.

As an organization matures, ad-hoc roles are often developed into full teams. This specialization is often critical for building internal economies of scale, but the formalization of new constituencies should be kept in check

Where a matrix indicates a high-touch relationship between two groups (e.g., a group of engineers working on a feature and the lawyers trying to ensure the legal compliance of that feature), efforts should be made to reduce the cost of that interaction by colocating their members (e.g., embed a lawyer with the engineers).

  1. Prioritize asynchronous information distribution over synchronous.

A significant source of failure demand for meetings and status updates is the desire of organizational leaders to keep abreast of who’s doing what. This situational awareness is indeed important, but trying to maintain it by calling meetings, messaging people on Slack, and catching people on the hallways is a significant systemic drag on organizational productivity.

A better model for staying informed of developments as the organization scales is for groups to publish status updates as part of the regular cadence of their work. Leaders can asynchronously read these updates and, should the need arise, initiate additional, synchronous conversation to ask questions, provide feedback, etc.

Synchronous meetings should be reserved for low-latency collaboration on complex issues; likewise, collaboration should be reserved for synchronous meetings.

Companies are groups of people being compensated for having to spend some of their finite lifetimes not being with their partners, children, pets, or super weird hobbies. They deserve to be members of organizations which honor that time by ensuring that their work has value and meaning.

Darklang: A New Way of Building Serverless Backends

Nerd sniped by my friends Yoann and Martin

Former CEO/co-founder of CircleCI, PhD in compilers and static analysis

Demo: Office Sign-in Application

Philosophy: Why Dark with CTO Paul Biggar

What is Dark?

Citizen Kane Lame

There’s been a few articles recently about Citizen, a crime and neighborhood watch app.

‘FIND THIS FUCK:’ Inside Citizen’s Dangerous Effort to Cash In On Vigilantism
As an LA wildfire broke out, Citizen CEO Andrew Frame saw it as an opportunity. They offered first $10,000, then up to $30,000 for a tip that lead to the suspect’s arrest.

But the man whose name and face they shared with the world was innocent.

Tech companies want to juice engagement metrics. For Citizen, that means more people reporting more incidents, regardless of if they’re real, or how solid the evidence is.

“It’s basically an anxiety sweatshop,” a Citizen source said. “On days when things are ‘slow,’ they relax the standards around incidents because a dip in incident count is really bad,” they added. The company sends congratulatory emails announcing which analysts reported the highest number of incidents, another source added.

After all, if users don’t feel safe, they’ll want to pay Citizen for protection.

Just like how social media and consumer products don’t want you to be happy. Why? If you were happy, you’d probably be spending time with friends, not doom scrolling Twitbook or buying things you don’t need.

“In a healthy society we are typically not incentivized to sensationalize mundane events and code them as crime. I can’t help but think it plays into people’s anxieties and fears and magnifies people’s fears of the other,” Gilliard said. “What’s really dangerous is the ways they’re starting to serve as infrastructure, where people start to feel like they have to use them to maintain society and order.”

Oh and also: Hacktivist posts massive Citizen scrape to dark web.

Obvious Bot is Obvious

✉️ Wrapping Up

Have questions, comments, or feedback? Just reply directly, I'd love to hear from you.

If you find this newsletter useful and know other people who would too, I'd really appreciate if you'd forward it to them 🙏

Thanks for reading!

@clintgibler @tldrsec