kmarekspartz

Expanding our capacity to respond to unforeseen changes

Upon observing that nine cubed is off by one from the number of days in two years, a calendar system emerged.


There are 9 nonths in a biennium, with 9 neeks in a nonth, and 9 days in a neek. There is a biennial holiday between bienniums. Every other biennium holiday is two days.

The first day of the biennium corresponds with March 1st on the Gregorian calendar. The biennial holiday occurs on even number years and is equivalent to February 28th (or February 28th and 29th on Gregorian leap years).

The biennium is expressed in base 9 as well, so the current (at time of writing) biennium would be:

\(\lfloor2025 \div 2\rfloor = 1012 = 1344_9\)

I wrote this on 30₉th (27₁₀)th day of the 5th nonth, on the 3rd neekend.

Handy conversions:

  • 3 neeks (27 days) is approximately a lunisolar month.
  • 1 nonth is approximately a quarter, or a season.

Representing a nonary calendar on a page isn't too different from a 9 by 9 Sudoku grid, which can be used to represent the neeks in a biennium, or the days in a nonth. This also means scheduling exclusive recurring tasks maps to a Sudoku puzzle. For example:

We have 9 tasks to do this nonth. We must do each task once per neek, but never on the same neekday as another neek. We also can't do the task during the same third of the neek as the other neeks in our third of the nonth.

The closest existing calendars to this system that I'm aware of are the Mayan and French Revolutionary calendars, if you want comparisons, though neither were taken to the nines.

Marginal maintenance costs are much more important than fixed initial costs when making business decisions

When making business decisions, people often use initial or fixed costs to justify their decision. While being difficult to estimate, they still are much easier to estimate than maintenance and marginal costs. However, in the long term, maintenance and marginal costs will outweigh initial and fixed costs, so careful attention must be paid to marginal maintenance costs.

Long term costs increase for marginal or maintenance costs, and increase significantly for marginal maintenance costs.

Let's consider each combination. While a given cost isn't categorically in one quadrant, these dimensions can be used to conceptualize relative costs of alternatives under consideration.

Fixed initial costs

These are one time costs that are always necessary to take a given path, even in an ideal world.

Marginal initial costs

These are inefficiencies in implementing a decision that are included each time you make similar decisions. Bureaucracy, redundant efforts, and technical debt are three examples.

Fixed maintenance costs

These are the base recurring costs that you must incur as a consequence of your decision. Flat membership or licensing fees are a good example.

Marginal maintenance costs

These are recurring costs that grow proportionally to the number of customers are affected the outcome of your decision. Hiring people or paying for more computers are examples of this. Minimizing these costs has more benefit than reducing the other three types because they are recurring and growing.


See also: The Equation of Software Design

Use themes to clarify your goals

When defining goals, I've found themes to be more effective than particular achievements. Themes give clarity of purpose and direction, even when the day to day tactical priorities change. Does my plan for the day fit into my theme?

On the other hand, measuring progress against a theme is difficult. Instead of measuring directly, we have to use proxies. Choose proxies carefully. Be comfortable changing your proxy measurements even when you maintain the theme.

No need to start from scratch; you can merge your repos and preserve history.


If you'd like to merge two (or more) git repositories together and preserve the commit histories of both, here's the script for you:

cd some-repo

git remote add other-repo git@other-repo.com:other-repo/other-repo.git
git fetch other-repo
git checkout other-repo/master

git checkout -b merge-other-repo
mkdir other-repo

for f in *; do
  git mv $f other-repo
done

# If you're making a merge request:

git merge master --allow-unrelated-histories

git push origin merge-other-repo

# Then make a merge request

# If you're pushing directly to master:

git checkout master

git merge merge-other-repo

git push origin master

A concurrent implementation Daytime protocol in Rust


When learning a language, I rewrite small programs I've previously written to jumpstart my learning. Implementing a concurrent Daytime server has proved particularly useful because it uses both sockets and threads. If a language has good socket and threading libraries, it is likely a good language.

Previously, I demonstrated a Haskell implementation. Here's an example in Rust:

extern crate time;

use std::time::Duration;
use std::io::Write;
use std::net::{TcpListener, TcpStream};
use std::thread;

fn handle_client(mut stream: TcpStream) {
    let date = time::strftime("%F %T\n", &time::now_utc()).unwrap().to_string();
    let _ = stream.write(date.as_bytes());
}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:13").unwrap();

    for stream in listener.incoming() {
        match stream {
            Ok(stream) =>  {
                thread::spawn(move || {
                    // connection succeeded
                    thread::sleep(Duration::new(1,0));
                    handle_client(stream)
                });
            }
            Err(_) => { /* connection failed */ },
        }
    }

    drop(listener);
}

Using a log-structured schema, we can merge SQL databases to achieve eventual consistency.


Previously, I introduced eventual consistency for SQL. This post illustrates how to normalize an eventually consistent SQL database.

To demonstrate how to normalize for eventual consistency, let's design a database for a Twitter clone, consisting of users and statuses. A traditional schema for a Twitter looks like:

CREATE TABLE Users (
  username VARCHAR(255) PRIMARY KEY,
  email VARCHAR(255),
  phone VARCHAR(255),
  location VARCHAR(255),
  confirmed BOOLEAN NOT NULL,
  salt VARCHAR(255) NOT NULL,
  hashed_password VARCHAR(255) NOT NULL
);

CREATE TABLE Statuses (
  content VARCHAR(140) NOT NULL,
  created_at DATE DEFAULT ( SYSDATE ) NOT NULL,
  username VARCHAR(255) REFERENCES Users (username) NOT NULL,
) PRIMARY KEY (content, created_at, user_id);

CREATE TABLE Follows (
  follower_username VARCHAR(255) REFERENCES Users (username) NOT NULL,
  followee_username VARCHAR(255) REFERENCES Users (username) NOT NULL
) PRIMARY KEY (follower_username, followee_username);

The first step in normalizing for eventual consistency is to identify state changes in your data. For example, a user becomes confirmed after clicking a link in an email or text message. Under the schema above, the following UPDATE statement would get executed:

UPDATE Users
SET confirmed = true
WHERE username = 'kmarekspartz';

However, since we're avoiding UPDATE, this will not work. Instead, let's normalize this mutation out of our database.[^1]

[^1]: I'm going to assume offline migrations for simplicity, but these migrations can be achieved in a zero-downtime environment, too. You would create both places for the data reside, deploy a version of the application to read from both, deploy a version of the application to write to both, run a backfill migration (like in the example), then deploy a version which only reads and writes the new place, then drop the old place. Fun!

CREATE TABLE Confirmations (
  username VARCHAR(255) REFERENCES Users (username) NOT NULL
);

INSERT INTO Confirmations
SELECT username
FROM Users
WHERE confirmed = true;

ALTER TABLE Users
DROP COLUMN confirmed;

INSERT INTO Confirmations VALUES ('kmarekspartz');

SELECT Users.*, IS_NULL(Confirmations.username) AS confirmed
FROM Users
LEFT OUTER JOIN Confirmations
ON Users.username = Confirmations.username;

Applying this normalization to the rest of the schema would lead to a new schema:

CREATE TABLE Users (
  username VARCHAR(255) PRIMARY KEY,
  salt VARCHAR(255) NOT NULL,
  hashed_password VARCHAR(255) NOT NULL
);

CREATE TABLE Confirmations (
  username VARCHAR(255) REFERENCES Users (username) NOT NULL
);

CREATE TABLE Emails (
  email VARCHAR(255) PRIMARY KEY
);

CREATE TABLE UserEmails (
  username VARCHAR(255) REFERENCES Users (username) NOT NULL,
  email VARCHAR(255) REFERENCES Emails (email) NOT NULL
) PRIMARY KEY (username, email);

CREATE TABLE Phones (
  phone VARCHAR(255) PRIMARY KEY
);

CREATE TABLE UserPhones (
  username VARCHAR(255) REFERENCES Users (username) NOT NULL,
  phone VARCHAR(255) REFERENCES Phones (phone) NOT NULL
) PRIMARY KEY (username, phone);

CREATE TABLE Locations (
  location VARCHAR(255) PRIMARY KEY
);

CREATE TABLE UserLocations (
  user_location_id PRIMARY KE