Conflict in the matrix

From Apenwarr’s “What do executives do, anyway?”:

To paraphrase the book [High Output Management, by Andy Grove], the job of an executive is: to define and enforce culture and values for their whole organization, and to ratify good decisions.

That’s all.

Not to decide. Not to break ties. Not to set strategy. Not to be the expert on every, or any topic. Just to sit in the room while the right people make good decisions in alignment with their values. And if they do, to endorse it. And if they don’t, to send them back to try again.

There’s even an algorithm for this.

It seems too easy to be real. For any disagreement, identify the lead person on each side. Then, identify the lowest executive in the corporate hierarchy that both leads report into (in the extreme case, this is the CEO). Set up a meeting between the three of them. At the meeting, the two leads will present the one, correct decision that they have agreed upon. The executive will sit there, listen, and ratify it.

But… wait. If the decision is already made before the meeting, why do we need the meeting? Because the right decision might not happen without the existence of that meeting. The executive gives formal weight to a major decision. The executive holds the two disagreeing leads responsible: they must figure out not what’s best for them, but what’s best for the company. They can’t pull rank. They can’t cheat. They have to present their answer to a person who cares about both of their groups equally. And they want to look good, because that person is their boss! This puts a lot of pressure on people to do the right thing.

From HBR’s “Problems of Matrix Organizations”:

Another possible source of decision strangulation in matrix organizations occurs when managers frequently or constantly refer decisions up the dual chain of command. Seeing that one advantage of the conventional single chain of command is that two disagreeing peers can go to their shared boss for a resolution, managers unfamiliar with the matrix worry about this problem almost more than any other. They look at a matrix and realize that the nearest shared boss might be the CEO, who could be five or six echelons up. They realize that not too many problems can be pushed up to the CEO for resolution without creating the ultimate in information overload. So, they think, will not the inevitable disagreement lead to a tremendous pileup of unresolved conflict?

Certainly, this can happen in a malfunctioning matrix. Whether it does happen depends primarily on the depth of understanding that exists about required matrix behavior on the part of managers in the dual structure. Let us envision the following scene: a manager with two bosses gets sharply conflicting instructions from his product and his functional bosses. When he tries to reconcile his instructions without success, he quite properly asks for a session with his two bosses to resolve the matter. The three people meet, but the discussion bogs down, no resolution is reached, and neither boss gives way.

The two bosses then appeal the problem up a level to their respective superiors in each of the two chains of command. This is the critical step. If the two superiors properly understand matrix behavior, they will first ascertain whether the dispute reflects an unresolved broader policy issue. If it does not, they know their proper step is to teach their subordinates to resolve the problem themselves—not to solve it for them. In short, they would not let the unresolved problem escalate, but would force it back to the proper level for solution, and insist that the solution be found promptly.

Often, conflict cannot be resolved; it can, however, be managed, which it must be if the matrix is to work. Any other course of action would represent management’s failure to comprehend the essential nature of the design.

Lastly, Alex Komoroske’s deck on Slime Molds is good.


Behaving like the character in a story

I enjoyed reading Robert Kolker’s “Who Is the Bad Art Friend?”, specifically about the non-symmetric relationships of fiction and inspiration.

Larson’s biggest frustration with Dorland’s accusations was that they stole attention away from everything she’d been trying to accomplish with this story. “You haven’t asked me one question about the source of inspiration in my story that has to do with alcoholism, that has to do with the Chinese American experience. It’s extremely selective and untrue to pin a source of a story on just one thing. And this is what fiction writers know.” To ask if her story is about Dorland is, Larson argues, not only completely beside the point, but ridiculous. “I have no idea what Dawn is thinking. I don’t, and that’s not my job to know. All I can tell you about is how it prompted my imagination.”

When Larson discusses “The Kindest” now, the idea that it’s about a kidney donation at all seems almost irrelevant. If that hadn’t formed the story’s pretext, she believes, it would have been something else. “It’s like saying that ‘Moby-Dick’ is a book about whales,” she said. As for owing Dorland a heads-up about the use of that donation, Larson becomes more indignant, stating that no artist has any such responsibility. “If I walk past my neighbor and he’s planting petunias in the garden, and I think, Oh, it would be really interesting to include a character in my story who is planting petunias in the garden, do I have to go inform him because he’s my neighbor, especially if I’m still trying to figure out what it is I want to say in the story? I just couldn’t disagree more.”

“[Dorland] might behave like the character in my story,” [Larsen] said. “But that doesn’t mean that the character in my story is behaving like [Dorland].”

Captain Awkward also had memorable advice:

Creatively speaking, I think you can generally write whatever you want about whoever you want. You do not owe people flattering portrayals in your fiction and you don’t owe the world a fair hearing of both sides. Even if you’re writing memoir, the expectation is that you’ll write the truth about your own experiences and recollections of events. So, when it’s just you and a blank page that nobody else will see? Name names. Settle scores. Spill some beans. Layer in all the carefully hoarded observations and details you’ve been saving up. You don’t have to solve publishing problems right this second, so feed the fire in your belly with your ex’s pet name for himself if that’s what gets it done; spite is motivating. You can always pull up a name generator later when you need to.

Ethically speaking, being free to write whatever you want does not mean being free of consequences from what you wrote. If you include recognizable details about real people in your published writing, fictional or otherwise, if you disclose confidential information about them (like mental health diagnoses), assume that somebody will connect the dots, and assume that the people you wrote about will eventually feel some kind of way about it. They may not have grounds to sue you, but they might think you are a bad person or a lazy artist, and they might tell their own stories where you are not the hero.


TIL: Detecting block returns in Ruby

I was doing some research on introspecting Ruby on Rails database transactions for a Reddit thread, and came across this Rails PR that had some new Ruby behavior for me: detecting an early return from a block.

Some background: A Ruby language feature, that can frequently surprise people, is that using return within a Ruby block will return not only from the block itself, but also from the block’s caller too. Using next is really the only truly safe way to interrupt a block early; even break can be troublesome if the block is called by an enumerator. Also, next can take a return value too, just like return e.g. next my_value.

I found the Rails PR interesting, because it has a method for detecting and warning on an early return. Here’s a simplified example:

def some_method(&block)
  block.call
  completed = true # won't be called if the block returns early
ensure
  if completed
    puts "ok"
  else
    puts "returned early"
  end
end

some_method { return } # => "returned early"
some_method { next } # => "ok"

This works because the methods ensure block will always be called, even if #some_method returns early. That was a novel implementation for me.


Home buyer letter

Having now successfully purchased a home in San Francisco, I have had friends asked me for resources. This was the letter we sent to the seller, which is very SF, but also the energy to bring. I also learned that these letters are waning because they are biased af.

Dear [seller],

Your home at [street address] is the right fit for us, our two cats, and (hopefully) our future family. We love the long layout, high ceilings (we’re both tall) and sunny patio, and look forward to hosting friends for Sunday brunch someday.

We’re rooted in the city through the South End Rowing Club, St. Francis Lutheran Church, supporting the YMCA, kickstarting local businesses like Andytown coffee, and working at tech companies and nonprofits. Ben worked for three years just around the corner at California and Grant, and Angelina has worked at numerous tech companies around Union Square and SOMA. We love the quiet contrast of Nob Hill against the rest of downtown San Francisco.

We see SF as a long term home and [street address] as a homebase for work and play: swimming and rowing on the SF Bay, and wine trips up to Napa and Sonoma too. Your home would have the perfect commute for Ben, walking down the hill to his nonprofit job at [organization] where he builds technology to help people apply for food stamps, remove marijuana convictions from criminal records, and improve how government delivers services to those who need them most. And it’s a short walk for Angelina to the [company] Shuttle or Downtown SF [company] office where she does business development for maps and location technology in use around the world.

Your home would provide the greatest fit for us as we aim to continue to contribute to the fabric of the city, where we hope to raise a family and be active in the community. We aim to take great care of an already-loved property while advocating for a neighborhood that supports all members throughout the growth of our city.

Our offer is fair given the condition, amenities and comparable sales, and we aim for being the best buyer to steward this home and the neighborhood. We look forward to your consideration of our offer for [street address].

Sincerely,
Ben and Angelina


GoodJob, what’s new: Cron, concurrency controls, and a dashboard demo (v1.12.0)

This is a quick roundup of what’s new with GoodJob v1.12.0 since the last update published for GoodJob v1.9.

GoodJob ( github ) is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails. If you’re new to GoodJob, read the introductory blog post.

For further details on the following updates, check out GoodJob’s Changelog or Readme.

Cron-like replacement for repeating/recurring jobs

GoodJob now ships with a cron-like replacement for repeating/recurring jobs. The cron-like process runs either via the CLI, or async within the web server process. Repeating jobs can be scheduled to the second, powered by the Fugit gem. Here’s what the configuration looks like:

# config/environments/application.rb or a specific environment e.g. production.rb

# Enable cron in this process; e.g. only run on the first Heroku worker process
config.good_job.enable_cron = ENV['DYNO'] == 'worker.1' # or `true` or via $GOOD_JOB_ENABLE_CRON

# Configure cron with a hash that has a unique key for each recurring job
config.good_job.cron = {
  # Every 15 minutes, enqueue `ExampleJob.set(priority: -10).perform_later(52, name: "Alice")`
  frequent_task: { # each recurring job must have a unique key
    cron: "*/15 * * * *", # cron-style scheduling format by fugit gem
    class: "ExampleJob", # reference the Job class with a string
    args: [42, { name: "Alice" }], # arguments to pass; can also be a proc e.g. `-> { { when: Time.now } }`
    set: { priority: -10 }, # additional ActiveJob properties; can also be a lambda/proc e.g. `-> { { priority: [1,2].sample } }`
    description: "Something helpful", # optional description that appears in Dashboard (coming soon!)
  },
  another_task: {
    cron: "0 0,12 * * *",
    class: "AnotherJob",
  },
  # etc.
}

Concurrency controls

GoodJob now offers an ActiveJob extension to provide customizable limits on the number of jobs enqueued or executed concurrently. Rails might upstream it too. Here’s how to configure it:

# app/jobs/my_job.rb
class MyJob < ApplicationJob
  include GoodJob::ActiveJobExtensions::Concurrency

  good_job_control_concurrency_with(
    # Maximum number of jobs with the concurrency key to be concurrently enqueued
    enqueue_limit: 2,

    # Maximum number of jobs with the concurrency key to be concurrently performed
    perform_limit: 1,

    # A unique key to be globally locked against.
    # Can be String or Lambda/Proc that is invoked in the context of the job.
    # Note: Arguments passed to #perform_later must be accessed through `arguments` method.
    key: -> { "Unique-#{arguments.first}" } #  MyJob.perform_later("Alice") => "Unique-Alice"
  )

  def perform(first_name)
    # do work
  end
end

Dashboard Demo

Don’t take my word for what a good job it is. Check out the new GoodJob Dashboard demo running on Heroku entirely within a single free dyno: https://goodjob-demo.herokuapp.com/

More news:

  • Wojciech Wnętrzak aka @morgoth became a GoodJob maintainer.
  • Wrote up details of the evolving development philosophy behind GoodJob.
  • The Dashboard now allows removing jobs, with more actions coming soon.

Contribute

Code, documentation, and curiosity-based contributions are welcome! Check out the GoodJob Backlog , comment on or open a Github Issue, or make a Pull Request.

I also have a GitHub Sponsors Profile if you’re able to support GoodJob and me monetarily. It helps me stay in touch and send you project updates too.


Paranoid and Reparative readings

From How Twitter can ruin a life: Isabel Fall’s sci-fi story “I Sexually Identify as an Attack Helicopter” drew the ire of the internet:

The delineation between paranoid and reparative readings originated in 1995, with influential critic Eve Kosofsky Sedgwick. A paranoid reading focuses on what’s wrong or problematic about a work of art. A reparative reading seeks out what might be nourishing or healing in a work of art, even if the work is flawed. Importantly, a reparative reading also tends to consider what might be nourishing or healing in a work of art for someone who isn’t the reader.


GoodJob Updates v1.5 - v1.9: Dashboard, daemonize, async_server, and graceful shutdowns

This is a quick roundup of what’s new with GoodJob since the last update published for GoodJob v1.4.

GoodJob (github) is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails. If you’re new to GoodJob, read the introductory blog post .

For further details on the following updates, check out GoodJob’s Changelog or Readme.

GoodJob v1.5: Web Dashboard and configuration

GoodJob ships with a web dashboard to display future, finished and errored jobs for easy inspection. The Dashboard mounts as a self-contained Rails Engine.

GoodJob uses more Rails.application.config... for improved autoloading compatibility; deprecation notices have examples for updating configuration.

GoodJob v1.6: Daemonize

GoodJob can run as a backgrounded daemon for folks who are still managing servers with --daemonize.

GoodJob v1.7: Scheduled job cache

GoodJob caches scheduled jobs (i.e. ExampleJob.set(wait: 30.minutes).perform_later) for signifiantly improved latency without relying upon polling.

GoodJob v1.8: Graceful shutdown

GoodJob added additional shutdown options, including GOOD_JOB_SHUTDOWN_TIMEOUT to allow jobs to finish before exiting.

GoodJob v1.9: async_server mode

GoodJob added an additional async execution mode to simplify the default configuration: running jobs as part of the web-process (and not console, Rake commands, etc.)

Upcoming: cron and concurrency controls

GoodJob will add support for cron-style repeating jobs, and concurrency controls to ensure that only a specified number (1 or more) jobs are enqueued or performed at the same time. I previously was opposed to extending ActiveJob’s interface, but have changed my mind; it’s on!

Contribute

Code, documentation, and curiosity-based contributions are welcome! Check out the GoodJob Backlog, comment on or open a Github Issue, or make a Pull Request. Thank you!!! to everyone who has contributed to GoodJob, including morgoth, tedhexaflow, weh, lauer, reczy, zealot128, gadimbaylisahil, Mr0grog, thilo, arku, sj26, jm96441, thedanbob, and joshmn.

I also have a GitHub Sponsors Profile if you’re able to support GoodJob and me monetarily. It helps me stay in touch and send you project updates too.


Is SecureRandom.choose the wrong name for generating randomized strings with Ruby?

I frequently need to generate a short, random string:

  • I need a non-numeric, non-enumerable url ID. e.g. https://example.com/resources/TOKEN
  • I need to generate a short, human-readable random activation token and I don’t want it to contain similar-looking characters like 1I or O0
  • I want to do something fun with strings of emojis.

I’ve been copy-pasting this snippet around for years:

def random_string(alphabet:, length:)
  Array.new(length) { alphabet.chars[rand(alphabet.chars.size)] }.join
end

random_string(alphabet: "AaBbCc123", length: 7) 
# => "C113A11", "Abcc3B2", "33cabbC", etc.

random_string(alphabet: "😀😍🙃🤪😎", length: 3)
# => "😍😀😎", "😀🤪🤪", "🙃😎😀", etc.

The gems friendly_id and uniquify can do this too.

There was recently a discussion on Ruby on Rails Link Slack about generating random strings, and I went looking to see if there was a better implementation.

I found SecureRandom.choose, which is built into the Ruby standard library, and as part of the SecureRandom module, should be fairly trustworthy. Looks perfect:

# SecureRandom.choose generates a string that randomly draws from a source array of characters.
#
# The argument _source_ specifies the array of characters from which to generate the string.
# The argument _n_ specifies the length, in characters, of the string to be generated.
#
# The result may contain whatever characters are in the source array.
#
#   require 'securerandom'
#
#   SecureRandom.choose([*'l'..'r'], 16) #=> "lmrqpoonmmlqlron"
#   SecureRandom.choose([*'0'..'9'], 5)  #=> "27309"

…but there is a problem: SecureRandom.choose is a private method. The choose method is used to implement the public SecureRandom.alphanumeric method, but is not itself exposed publicly. I went back to the initial feature request and found the reason:

I feel the method name, SecureRandom.choose, doesn’t represent the behavior well.

Fair enough. Until the name is figured out, I’ll still be using it:

require 'securerandom'

SecureRandom.send :choose, "😀😍🙃🤪😎".chars, 3
# => "😍😎😍"

Generic advice for managers

From Allison Green’s Ask a Manager: “I Got a Terrible Review on Glassdoor, and I’m Spiraling”:

The reality is, when you’re a manager, not everyone will like working for you. You could be the greatest boss in the world and some people still wouldn’t like you. Partly that’s because being a good manager means giving feedback, addressing problems, and holding people accountable in ways they might not like. If you need to correct someone a lot, or have difficult conversations with them about their work, or say no to something they wanted, it’s human nature that you might not end up being their favorite person. Or you might have a style that doesn’t align well with theirs — maybe you’re very direct and matter-of-fact and that feels brusque to them, or maybe you’re a planner and a devotee of process and they work more spontaneously. That wouldn’t mean either of you is in the wrong; it would just mean you don’t mesh well together.

Sometimes, too, people dislike a job or a manager for reasons that aren’t as much about the manager as they are about other things going on with that person — a dislike of their career path, stressors outside of work, a generally bad fit with the role, or all kinds of things.

Or, frankly, you might be an imperfect manager — most of us are — but that doesn’t mean you’re a horrible one. Managing people is hard, and every manager will get things wrong now and then. Ideally you’ll establish a track record of fairness, transparency, and good judgment so your mistakes are judged within that context … but you still might encounter an employee who judges your mistakes harshly. You’re basically on a stage when you’re the boss; you’re going to be scrutinized by the people under you, and there will be things they take issue with. It’s part of the job, and you’ve got to be okay with that.

Or, yes, you might be a terrible manager! It’s possible. There are lots of terrible managers out there. But I’m skeptical that you’re terrible in the specific ways the review described (mean and intolerant of mistakes), because your detailed explanation of your approach to mistakes sounds pretty healthy and because you sound genuinely thoughtful and caring toward team members. People can delude themselves, of course, and managers aren’t always reliable narrators of their own management styles. But the way you talk about how you operate — and your reaction now — doesn’t seem to line up with that review or with the feedback people have given your boss about you. That doesn’t mean that review is definitely wrong. I obviously can’t say that with certainty. But I don’t think it warrants the self-flagellation you’re doing.

That said, all managers have ways they could improve — things they’re doing that irritate or upset their teams, or make things run less effectively, or allow problems to fester. I can say with confidence that you could be a better manager, because we all could. So one option is to take this as an impetus to do a real inventory of the way you manage, figure out where you could improve, and lean into doing that work.


GoodJob 1.4: JRuby compatibility and more

GoodJob version 1.4 is released. GoodJob is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails. If you’re new to GoodJob, read the introductory blog post .

GoodJob’s v1.4 release adds support for:

  • JRuby
  • MRI 3.0
  • Rails 6.1
  • Postgres 12+

And includes additional improvements to the Web Dashboard.

Version 1.4’s release comes three months after v1.3 and five months after GoodJob’s initial v1.0 release.

JRuby compatibility

GoodJob 1.4 adds compatibility for Ruby on Rails applications running under JRuby. GoodJob’s multithreading is built on top of ConcurrentRuby, which provides consistent behavior and guarantees on all four of the main Ruby interpreters (MRI/CRuby, JRuby, Rubinius, TruffleRuby).

A minor downside of JRuby is that JRuby’s database adapter, activerecord-jdbc-adapter, does not support Postgres LISTEN, which means GoodJob must rely on polling for new jobs under JRuby.

Broader compatibility and improvements

In addition to JRuby, GoodJob 1.4 adds compatibility for MRI 3.0, Rails 6.1, and Postgres 12+.

GoodJob 1.4 also includes improved filtering for the Web Dashboard,.

What’s next

The next version of GoodJob will add support for daemonization.

Contribute

Code, documentation, and curiosity-based contributions are welcome! Check out the GoodJob Backlog , comment on or open a Github Issue, or make a Pull Request.

I also have a GitHub Sponsors Profile if you’re able to support GoodJob and me monetarily. It helps me stay in touch and send you project updates too.