Skip to content

The joy of removing code

I am working on a project right now where my main task is to remove code. “What? I thought developers were supposed to add code, not remove it?” Well in some cases removing code actually can help a project. Some reasons to remove code: if the code doesn’t serve any purpose, if it isn’t executed, if it is for an edge case that never happens, if it has been superseded.

Removing code is easier with automated tests, but I still find myself using a combination of automated tests, manual testing, and the find command. (This codebase is ruby, if I was using java I’d use the type system.) It’s painstaking work, but will be good in the end.

My steps for removing code:

  • Start with a plan and and end goal in mind. Otherwise it can get overwhelming if you are dealing with a system of any size.
  • Create a feature branch
  • Identify one piece of code you’re going to either remove or keep
  • Make sure you know where it is called. If it isn’t called anywhere, remove it.
  • Remove any tests and associated functionality (views, helpers, etc).
  • Examine any state managed by the code for removal (database tables, etc)
    • Oftentimes I’ll just note that this should be removed in a few months, and focus on hiding the UX. It’s a lot easier to resurrect UX if you make a mistake than it is a dropped database table.
  • Commit the changes with a good commit message.
  • Fan out from there and see if you need to add anything to your list.

Take your time, don’t rush it.

Code that never runs can’t have any bugs, and is super fast. Think about removing some code today.

A follow up to “Deeply problematic but practical advice”

I linked to the first article Charity wrote, and wanted to link to her follow on piece/”post mortem”. (In technical terms, a post mortem is an examination of a problem or system failure in hopes of avoiding the situation in the future.) From the post, she encountered some very harsh words from the Internet:

I have never received textual scrutiny of this type before, where every single word was turned over and macerated and peered at for evidence of traitorous views. It sucks.

Lots of good stuff there about the reactions to her original post, her takeaways and how she would do some things differently next time. Worth the read.

When you manage, do unto others as they’d have you do unto them

A famous person once said “do unto others as you’d have them do unto you”. That is, treat others like you’d like to be treated.

As a manager, heck as someone who wants to get things done in an organization, that’s the baseline. If you can’t treat other people with respect and empathy, you’re going to have a hard time getting anything done. You might be able to get some stuff done by yourself, or via transactional behavior, or via fear. (By the way, if this is common in your organization, I’d look for new work because this type of motivation is symptomatic of a toxic workplace.) To really build a strong team, you need to, at a minimum, treat others as you’d treat yourself.

However, to be effective as a manager, this isn’t enough. Rather than treating others as you’d like to be treated, you need to learn how others would like to be treated. And then treat them that way.

What does that mean? Well, within the context of a software development team (where I have had direct management experience) you need to figure out what motivates each member of your team and, within the limits of your organizational capacity, give that to them. “Jane” may be motivated by the opportunity to work on the front end, whereas “Biyu” may want to focus on her algorithms. Some team members may want to start new projects, others may want to refine processes and make existing applications work really well.

This motivation changes over time, of course. This introduces additional complexity. When “Bob” joined the team, he might have been motivated by the opportunity to work with new technologies, but a few years in he may be more motivated by really impacting user experience.

The best way to figure out what motivates someone is to ask them. Hence the emphasis on 1:1s in the software business, where the employee controls the agenda. Another good way to decipher motivation is observation. Does “Olaf” spend extra time working on project A but is dragging his feet on project B? If so, why? Is one more UX focused, and the other more algorithmic? Such observations can also be starting points during the 1:1: “you seem to really be enjoying project A. What do you like most about that work?”

If your team or company can’t offer what someone wants, then it may be time for them to move on. The best managers I know were excited when I moved on because I judged it’d make me happier. This isn’t easy for the organization, but if you are playing the long game, the former employee will be an ambassador for you. Whether that is referring business or other employees, the goodwill can benefit the company. Contrast that with a company that holds on to a valuable employee even if they are unhappy (with golden handcuffs or other less savory mechanisms). When the employee finally leaves, there will be no reservoir of goodwill. Even before linkedin and glassdoor, this was no good. Now that folks can look up where people previously worked (and possible employees should do their research), the way you treat folks will be shared.

Overall, the key is to realize that what may excite you, or would excite you if you were an individual team member, or what might have excited you at a particular stage of your career, may not excite a team member. So, rather than put them in your shoes, put yourself in theirs.

One hundred days of blogging every day

This is a meta post, but it’s been 100 days since I started blogging every day. I wanted to celebrate! And I just wanted to note some lessons.

When you blog every day, it doesn’t have to be as good

Before I started the streak, I blogged at least once a month for the previous year or two. During that time, I often spent a lot of time working on my blog post. Not necessarily days, but hours. This was, after all, the only blog post I was writing, so it needed to be really good. But once I committed to writing once a day, I was focused on getting something out. I still wanted to be proud of it, but there wasn’t as much pressure. It could even be something really short, or just a pointer to a different piece that I thought was interesting (like here or here).

Having a streak forces your hand

A streak makes you make time. There were definitely times when it was after dinner and I still hadn’t blogged. If I wasn’t trying to keep a streak going, I’d have skipped that day.

Opinion pieces are easier to write than technical pieces

It was easier (and more popular) to write a piece on the nature of the CTO/co-founder or what a senior engineer is than it was on an interesting rails gem like after_party or the nuances of Stripe Connect integration. Interviews were the easiest post to write, but required a bit more planning.

More content means more readers, but mostly from one offs

In the months before December, I averaged about 4200 visits/month. In the months after, I averaged about 6300, but most of the elevated traffic came on specific days from referral traffic due to posts I had made to Hacker News. Still, the increase in traffic was about 7% even for months when I didn’t have a post that “went viral”

I feel so lucky to have a place to spout off. Thanks for reading.

What is your BATNA?

When you are negotiating, you always want to be thinking about your Best Alternative to a Negotiated Agreement (BATNA). This applies in any negotiation, whether business, personal or even with yourself. When you have a better BATNA, you have more negotiating leverage and are more likely to get what you want out of it.

This is why when I was a contractor, I always had more than one client. Even if I was working with a good client who paid well, on time and was fun to work, I had more freedom if I had another client. Things might go south at the first client and I wouldn’t be out on the street. It’s also why I would always start looking for a contract 6-8 weeks before my current contract was finishing.

It’s why you should always get competing job offers. If you have a job offer and your best alternative is to keep job hunting, that’s not appealing. If, instead, you are choosing between two job offers, you are in a much better position. (No duh!)

It is also why it’s always easier to get a job if you have a job. The BATNA of declining a job offer when you have employment is, well, you remain your current position. Your current job may not be all that awesome (which is why you are looking) but for most folks being employed is a better alternative than being unemployed.

How can you use the concept of BATNA to improve your life?

First, be aware of the concept. Start to look at decisions in your life and think about the BATNA. Even small decisions, like ‘should I get coffee or nothing’? Or ‘what happens if I ask my wife to take out the garbage’? Or ‘should I ask for a raise’? In all of these cases, you can expect some kind of negotiation, and you can think about what the alternative is if that negotiation fails.

Second, take actions to improve your alternatives. If you are unemployed and want more leverage in the job hunt, start consulting. If your wife won’t take out the trash, can you improve your BATNA by making it easier to take out the trash yourself (maybe move the trash can into the garage)? Or building some kind of trash chute?

The concept of a BATNA is key to getting the most out of any negotiation. If you have good alternatives, you have more leverage to leave the negotiation, and if you don’t, you will need the negotiation to complete successfully.

More about BATNAs and salary negotiation here.

When should you leave a job?

Saw a great post by Kathy Keating, on a slack of which I am a member. This is how she suggests thinking about whether to stay or go at a given position:

1) Are there ethical issues? Leave.
 

2) Are there leadership issues?

 

2a) Can I have transparent/open dialog and is there hope that the leadership issue can be addressed? Timebox length of stay.

2b) No dialog. No hope of addressing issues. Leave.

 

3) Are there systemic company process issues?

 

3a) Company knows and is addressing the issue. Timebox length of stay.

3b) Company is fine being a mess. Leave.

 

4) Do I have career growth?

 

4a) No. Leave.

4b) Yes. Stay if no other blockers.

This is a lightweight framework to consider when you have a bad day or week at work. It’s important to acknowledge that:

  1. I (and most other software developers) are very privileged when it comes to job expectations. (My SO worked in healthcare and the stories, oh the stories.)
  2. The grass is not always greener and if you stay you might have credibility to effect change.

I think there are other factors that might play in here like type of work, location of work, and compensation, but in general I love this flow chart of how to decide whether or not to stay in a job. If there are other items that are important to you, add them into the decision tree, but having something like this will make it easier to make a hard decision like leaving a company.

PS No post about deciding to leave a job would be complete without referencing Shields Down and Bored People Quit.

Noticing blocked charges in stripe

We recently had our first blocked charge at The Food Corridor. We found out because Stripe kindly sent us a notice. It was blocked because Stripe’s algorithms determined that it was possibly fraudulent.

This is a pretty rare event (hopefully) but we still want to be prepared to deal with it. In the evolution of admin features, this is definitely at level 2, because it involves customers and money.

Luckily stripe_event has our backs and we can easily catch a webhook event and send an email if this happens. We just need to figure out what the event looks like.

I searched the stripe API but found nothing about blocked charges. I sent an email into Stripe’s helpful support system, and shortly thereafter someone had tracked down the event so that we could set up that webhook. Here’s the event (anonymized):

{
  "object": {
    "id": "ch_xxx",
    "object": "charge",
    "amount": 100,
    "amount_refunded": 0,
    "application": null,
    "application_fee": null,
    "balance_transaction": null,
    "captured": false,
    "created": 111,
    "currency": "usd",
    "customer": "cus_xxx",
    "description": "desc",
    "destination": "acct_xxx",
    "dispute": null,
    "failure_code": "card_declined",
    "failure_message": "Your card was declined.",
    "fraud_details": {
      "stripe_report": "fraudulent"
    },
    "invoice": null,
    "livemode": true,
    "on_behalf_of": "acct_xxx",
    "order": null,
    "outcome": {
      "network_status": "not_sent_to_network",
      "reason": "highest_risk_level",
      "risk_level": "highest",
      "rule": {
        "id": "block_if_high_risk__enabled",
        "action": "block",
        "predicate": ":risk_level: = 'highest'"
      },
      "seller_message": "Stripe blocked this charge as too risky.",
      "type": "blocked"
    },
    "paid": false,
    "receipt_email": null,
    "receipt_number": null,
    "refunded": false,
    "refunds": {
      "object": "list",
      "data": [
      ],
      "has_more": false,
      "total_count": 0,
      "url": "/v1/charges/ch_xxx/refunds"
    },
    "review": null,
    "shipping": null,
    "source": {
      ...
    },
    "source_transfer": null,
    "statement_descriptor": "descriptor",
    "status": "failed",
    "transfer_group": "group_ch_xxx"
  },
  "previous_attributes": null
}

The key is to look at the outcome type value, and handle blocked failed charges differently than normal failed charges (perhaps by contacting the customer).

Always break rails migrations into smallest chunks possible, and other lessons learned

So this was a bit of a sticky wicket that I recently extracted myself from and I wanted to make notes so I didn’t make the same mistake again. I was adding a new table that related two existing tables and added the following code

class CreateTfcListingPeople < ActiveRecord::Migration
  def change
    create_table :tfc_listing_people do |t|
      t.integer :listing_id, index: true
      t.string :person_id, limit: 22, index: true

      t.timestamps null: false
    end

    add_foreign_key :tfc_listing_people, :people
    add_foreign_key :tfc_listing_people, :listings

  end
end

However, I didn’t notice that the datatype of the person.id column (which is a varchar) was `id` varchar(22) COLLATE utf8_unicode_ci NOT NULL

This led to the following error popping up in one of the non production environments:

2018-02-27T17:10:05.277434+00:00 app[web.1]: App 132 stdout: ActionView::Template::Error (Mysql2::Error: Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and (utf8_general_ci,IMPLICIT) for operation '=': SELECT COUNT(*) FROM `people` INNER JOIN `tfc_listing_people` ON `people`.`id` = `tfc_listing_people`.`person_id` WHERE `tfc_listing_people`.`listing_id` = 42):

I was able to fix this with the following alter statement (from this SO post): ALTER TABLE `tfc_listing_people` CHANGE `person_id` `person_id` VARCHAR( 22 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL.

But in other environments, there was no runtime error. There was, however, a partially failed migration, that had been masked by some other test failures and some process failures, since there was a team handoff that masked it. The create table statement had succeeded, but the add_foreign_key :tfc_listing_people, :people migration had failed.

I ran this migration statement a few times (pointer on how to do that): ActiveRecord::Migration.add_foreign_key :tfc_listing_people, :people and, via this SO answer, I was able to find the latest foreign key error message:

2018-03-06 13:23:29 0x2b1565330700 Error in foreign key constraint of table sharetribe_production/#sql-2c93_4a44d:
 FOREIGN KEY (person_id)  REFERENCES people (id): Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types in the table and the referenced table do not match for constraint. Note that the internal storage type of ENUM and SET changed in tables created with >= InnoDB-4.1.12, and such columns in old tables cannot be referenced by such columns in new tables.
Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-foreign-key-constraints.html for correct foreign key definition.

So, again, just running the alter statement to change the collation of the tfc_listing_people table worked fine. However, while I could handcraft the fix on both staging and production and did so, I needed a way to have this change captured in a migration or two. I split apart the first migration into two migrations. The first created the tfc_listing_people table, and the second looked like this:

class ModifyTfcListingPeople < ActiveRecord::Migration
  def up
    execute <<-SQL
      ALTER TABLE  `tfc_listing_people` CHANGE  `person_id`  `person_id` VARCHAR( 22 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
    SQL

    add_foreign_key :tfc_listing_people, :people
    add_foreign_key :tfc_listing_people, :listings
  end
  def down
    drop_foreign_key :tfc_listing_people, :people
    drop_foreign_key :tfc_listing_people, :listings
  end
end

Because I’d hand crafted the fixes on staging and production, I manually inserted a value for this migration into the schema_migrations table to indicate that the migration had been run in those environments. If I hadn’t had two related but different migration actions, I might not have had to go through these manual gyrations.

My lessons from this episode:

  • pay close attention to any errors and failed tests, no matter how innocuous. This is a variation of the “broken window theory”
  • break migrations into small pieces, which are easier to debug and to migrate back and forth
  • knowing SQL and having an understanding of how database migrations work (they are cool, but they aren’t magic, and sometimes they leak) was crucial to debugging this issue

“I want it all and I want it now”

Do you want it all? Should you?

Jason Cole has some wise words on the various hires you can make to make your life easier as a technical founder.

There’s a myth in our industry that you can write code and manage people, and that every engineer should do both if they want to advance their career. While I know people who are good at both, they’re generally the rare kinds of people who can hold two opposing concepts in their minds at the same time. And they never get to straddle those two areas for long; they either become a senior people leader or a senior technologist.

This is the old CTO vs VP of Engineering split.

So, as you grow in a company as a technical founder (or even an early engineer) you need to make a decision. And when you are hiring, instead of searching for someone who can wear all the hats that you used to, find someone who can take two or three of your hats and put them on. It’s the beginning of the transition from generalists to specialists.

How would you build a simple CRUD app in 2018?

Interesting discussion on HN about how you’d build a simple CRUD app in 2018. (CRUD stands for Create, Read, Update and Delete of records in a system.) As you’d expect, no shortage of opinions, with a lot of specifics. Also lots of recommendations to use what you know, which is always a good idea. CRUD apps, where you are simply gathering data via a web interface, run the gamut of complexity and usability, but it’s hard to believe how powerful having a centralized repository of data can be.

And the nice thing is that once you get the data into the backed of  a CRUD app (which is likely a SQL compatible database, but could be any other kind of datastore) you can either combine that data with other systems, expose the data via an API, or both.

Here was my answer to the question:

I’d use Rails today. But I can see the value in Django or Express or ASP.Net or Spring Boot.

 

Criteria I’d think about:

 

What is going to be easy for my organization to support (both operationally and with future code changes).

 

What do I know/want to learn? If the CRUD app is complicated, then I want a tech I know well. If the CRUD app is simple, then I may want to experiment with a different technology (again, within the organizational support guardrails).