9
\$\begingroup\$

Like most teams, we take turns to speak in our daily stand-ups. When we gather around the sprint board, it's easy to know who speaks when - we start at one end and pass the role along the line.

At present, we're all working remotely, so there's no physical ordering that we would agree on. And we would get tired of using the same order every day, leading to diminished engagement. So we start the meeting by publishing the day's speaking sequence. I started with a random shuffle of the team members, but soon realised it would be better if any of us could generate the list, and we'd all get the same result. So I now use the date as a seed for shuffling.

I use a MD5 hash to quickly spread the entropy all around the random word (just using date +%F unmodified resulted in the same sequence every day, because all the variation is near the end), and xxd to convert its hexadecimal output into raw bytes.

#!/bin/bash

set -eu -o pipefail

team=(Alice Bob Charlie Dan Erin Frank)

# Header for today
date +%A:%n
# Team members in today's order
printf '%s\n' "${team[@]}" |
    shuf --random-source <(date +%F|md5sum|xxd -r -p)
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Obviously, this is the cryptography team, not my day-job team. \$\endgroup\$ Jan 22 at 7:14
  • 2
    \$\begingroup\$ That's a good idea! I'll add this one to my collection of Python "one-liners". python -c "import sys;from datetime import date;import random;team=sys.argv[1:];random.seed(date.today().toordinal());random.shuffle(team);print(' '.join(team))" Alice Bob Charlie Dan Erin Frank \$\endgroup\$ Jan 22 at 22:10
  • 1
    \$\begingroup\$ @Eric, I like the Python approach, and it has some advantages. So I ran with it! \$\endgroup\$ Jan 24 at 8:24
9
\$\begingroup\$

Some suggestions:

  1. #!/usr/bin/env bash is a more portable shebang line.
  2. I would use long options for set and xxd for readability. So set -o errexit -o nounset -o pipefail and xxd -revert -plain.
  3. If you quote the array members you can easily use full names.
  4. In case any remote workers are in another time zone offset you'll want to use a fixed one, such as TZ=Pacific/Auckland date +%F. If you want to be on the safe side, get the time zone where your meeting time is midday at that offset (in the same hemisphere if possible, to avoid a 2 hour offset half of the year). That way, anyone running this code within 12 hours of the meeting time will get the same result.
  5. The output of md5sum is already completely scrambled. Passing it to xxd doesn't make the randomness input any more random. As mentioned in the comments it may be that shuf only uses a very small part of that randomness, in which case xxd is justified. Unfortunately man shuf doesn't go into any detail about --random-source.
  6. You can randomize arguments to shuf: shuf --echo --random-source <(date +%F|md5sum|xxd -r -p) -- "${team[@]}" achieves the same without printf.
  7. --random-source=<(…) would make it obvious that it is a key/value pair rather than a flag followed by a file argument.
\$\endgroup\$
5
  • \$\begingroup\$ Thanks. I had to use xxd because the md5sum output is only hex digits, so lots of the bits are unvarying (shuf) has a tendency to use very few bits from the random source. Point 6 is new to me - I overlooked that shuf can use arguments as input, so thanks greatly for that. \$\endgroup\$ Jan 22 at 9:11
  • 1
    \$\begingroup\$ I was worried there wouldn't be enough to review in such a short script - constantly amazed by the quality of contributors here! :-) \$\endgroup\$ Jan 22 at 9:12
  • \$\begingroup\$ I agree with most of this, but the set line is pretty idiomatic for bash strict mode. redsymbol.net/articles/unofficial-bash-strict-mode \$\endgroup\$
    – chicks
    Jan 22 at 16:30
  • \$\begingroup\$ @chicks For sure, I just deviate from idiomatic whenever I think it makes the code better. \$\endgroup\$
    – l0b0
    Jan 22 at 19:08
  • \$\begingroup\$ Fair enough. I'm usually a fan of making the code self-explanatory, but in this case I usually just drop that link in a comment above the two lines of strict mode. \$\endgroup\$
    – chicks
    Jan 22 at 19:22
1
\$\begingroup\$

Consider using a different language

Prompted by a comment, I chose to re-implement in Python. This has a couple of advantages:

  • No external dependencies (xxd isn't part of a default Debian or Ubuntu system)
  • Perhaps portable to our peer teams using non-Linux platforms.

I didn't do anything to ensure consistent time zone handling, because my team are all in the same time zone. Even if we share with the other teams, none of those straddle the date line during working hours, so not a concern for us. But suggestions welcome in the comments to this answer, if you have them!

I was able to add a couple of features:

  • We have guests who are not part of the development team, but often attend and have useful information to contribute. These people always speak after the core team, in a specific order.
  • Attempt to copy to a clipboard, if we find one. This was previously done outside of the shuffle script itself, but doing it from Python increases our portability.

Note: I considered storing the team list in a file to separate code from data, but preferred to keep everything in the source file as it's then easier to share amongst the team.

#!/usr/bin/python

team = "Alice, Bob, Charlie, Dan, Erin, Frank".split(', ')
guests = "Eve, Mallory".split(', ')

import sys;
import datetime;
import random;

today = datetime.date.today()
random.seed(today.toordinal())
random.shuffle(team)

output = (today.strftime('%A:%n%n') +
          '\n'.join(team + guests))

print(output)

# Save to clipboard if we can (for pasting into team chat)
try:
    import pyperclip
    pyperclip.copy(output)
except:
    pass
\$\endgroup\$
2
  • 1
    \$\begingroup\$ It looks good. You can remove the semicolons at the end of imports. I didn't check if the distribution is fair to every team member. You could use a for loop for the next 1000 days, to check that every team member is equally distributed to each position. \$\endgroup\$ Jan 24 at 10:38
  • \$\begingroup\$ The semicolons are a remnant from unrolling your one-liner; I'll remove them. \$\endgroup\$ Jan 25 at 13:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.