• Finite State Machine

    A model of computation where there is exactly one state at a time of a fixed number states. The current state can be changed by transitioning to another. Using state machines has the benefit of 1) being easy to reason about what the program does e.g. security audit and 2) making obvious what a valid state is so the programmer doesn’t accidentally put into a ‘bad’ state i.e. reduce bugs.

    Example applications:

    • Modeling authentication flows using a deterministic state machine to prove it can not be exploited
    • Modeling a user interface that has several modes to prevent bugs where a user gets stuck in an unrecoverable state

    The following example uses rustlang’s type system to model a finite state machine where transitions can be passed additional typed arguments:

    pub struct StateMachine<T> {
        pub state: T
    }
    
    pub struct StateMachineWithArgs<T> {
        pub state: T,
    }
    
    pub trait TransitionFrom<T, R> {
        fn transition_from(state: T, args: R) -> Self;
    }
    
    pub trait State {
        type Args;
        fn new(args: Self::Args) -> Self;
    }
    
    impl<T> StateMachineWithArgs<T> where T: State {
        pub fn new(state: T) -> Self {
            StateMachineWithArgs { state }
        }
    }
    
    struct StateOne;
    impl State for StateOne {
        type Args = ();
    
        fn new(args: Self::Args) -> Self {
            Self {}
        }
    }
    
    struct StateTwo;
    impl State for StateTwo {
        type Args = ();
    
        fn new(args: Self::Args) -> Self {
            Self {}
        }
    }
    
    struct StateThree;
    impl State for StateThree {
        type Args = ();
    
        fn new(args: Self::Args) -> Self {
            Self {}
        }
    }
    
    // Implement state transitions by implementing the TransitionFrom trait.
    // Doing this enforces only known transitions at compile time i.e.
    // you get a compile time error that two states can't be transitioned to
    
    impl TransitionFrom<StateMachineWithArgs<StateOne>, ()> for StateMachineWithArgs<StateTwo> {
        fn transition_from(state: StateMachineWithArgs<StateOne>, args: ()) -> StateMachineWithArgs<StateTwo> {
            StateMachineWithArgs::new(StateTwo::new(args))
        }
    }
    
    impl TransitionFrom<StateMachineWithArgs<StateTwo>, ()> for StateMachineWithArgs<StateThree> {
        fn transition_from(state: StateMachineWithArgs<StateTwo>, args: ()) -> StateMachineWithArgs<StateThree> {
            StateMachineWithArgs::new(StateThree::new(args))
        }
    }
    
    mod test_state_machine {
        use super::*;
    
        #[test]
        fn it_works() {
            // Initialize the state machine starting with the first state
            let state_one = StateMachineWithArgs::new(StateOne::new(()));
    
            // Transition to the next state
            let state_two = StateMachineWithArgs::<StateTwo>::transition_from(state_one, ());
    
            // This won't compile because it's not a valid transition!
            StateMachineWithArgs::<StateThree>::transition_from(state_one, ());
        }
    }
    

    In python with typing:

    import typing as t
    from functools import singledispatch
    from dataclasses import dataclass
    
    
    @dataclass
    class State1():
        check_me: bool
    
    class State2():
        pass
    
    class State3():
        pass
    
    class State4():
        pass
    
    State = t.Union[
        State1,
        State2,
        State3,
        State4,
    ]
    
    @singledispatch
    def transition(state: State) -> t.Optional[State]:
        pass
    
    @transition.register
    def state1_to_state2(state: State1) -> t.Optional[t.Union[State2, State3]]:
        if state.check_me:
            return State2()
        return State3()
    
    @transition.register
    def state2_to_state3(state: State2) -> t.Optional[State3]:
        return State3()
    
    @transition.register
    def state3_to_state4(state: State3) -> t.Optional[State4]:
        return State4()
    
    # Run the state machine to completion
    
    transition(transition(transition(State1(check_me=True))))
    transition(transition(State1(check_me=False)))
    

  • Cassowary Constraint Solver

    An algorithm used for constraining elements in a UI such as adjusting layout based on the screen size. It is designed to handle linear equality and inequality efficiently (e.g. show window to the left of the other and gracefully degrading if there isn’t space).

    Read the paper

    See also:

    • tui-rs uses Cassowary for laying out text-based user interfaces in the terminal
    • GraphPlan - another kind of constraint solver that uses both a novel data representation of the problem domain and a solver

  • Product Work Is a Pursuit of Facts About the User, Market, and Their Problems

    When building products you are always learning new things about the user, the market, and their problems. Sometimes this happens intentionally (e.g. doing user research) and sometimes it happens unintentionally (e.g. adding a feature that suddenly takes off in usage). Ideally these facts are made explicit and is accretive over time so that new facts leads to better understanding over time which leads to more successful products. This also requires flexibility and updating ones model as new information is uncovered.

    This is similar to the ideas from lean startup where the goal is to efficiently validate critical assumptions by building the minimum required to learn from users.

    Facts don’t necessarily need to be provable to be valuable, just correct (a G-statement). GΓΆdel Incompleteness For Startups argues that the unprovable yet true facts are the most scarce and therefore most valuable.


  • Startups Value Generalists Early, Specialists Later

    In the early stage of a startup, companies have limited resources and a wide range of things that need to get done. Founders often wear ‘multiple hats’ to build and run the company. This follows to early employeesβ€”even with a stated job role, out of necessity they will need to go beyond it. This appeals to generalists (those who can do multiple kinds of things reasonably well) and is highly valued because the startup needs to do a wide range of things with limited resources.

    Contrast that to a startup that is further along (> 1000 employees) and the needs change. Organization and administrative functions are required to coordinate many people working towards a shared set of goals. This puts pressure to hire the right person for a specific role where roles are defined to help administrators make sense of their workforce. Needs become more specific, and specialists are valued more because 1) they fit the definition of what’s needed more clearly 2) deeper expertise is required to solve more specific problems immediately.


  • Agent-Based API

    A way of modeling an external API provider as a compute resource for executing an agent program. Instead of making a request and getting a response (as in the usual HTTP API pattern) you submit a program that executes in the provider’s environment. This could allow users of an API to simplify their access patterns, benefit from data locality, and have stronger guarantees for execution than hosting their own infrastructure.

    For example, instead of making a read request to get some data then make a write based on that data and wait for an async webhook, the requestor could submit a program that does all of that executes within the providers contained environment.

    See also:


  • Juice

    In game design and development, juice is a term for the details that make the game visually interesting and exciting to interact with. Examples include screen shakes, subtle animations, music and sound effects, personality, etc. In the talk ‘Juice it or lose it’, Martin Jonasson and Petri Purho walk through making a rudimentary brick breaker game and adding juice to make it much more appealing.


  • Games Don't Generalize Well to a Client-Server Model

    In video game programming it can be tempting to replicate a client server model similar to how websites work. This is generally a mistake because game state tends to be highly co-mingled between state and graphics. Attempting to cleanly separate the ‘backend’ game logic with ‘frontend’ visuals results in awkward boundaries between the two, which are still coupled, but in a way that requires more complicated code to manage (e.g. a message bus, event sourcing) and are probably less performant (e.g. passing json messages within a 33ms frame budget).


  • UXR

    User experience research (UXR) is a function that works with users and analyzing data to learn about and test ideas. This serves as a way to avoid common biases when building products and making decisions (e.g. confirmation bias, availability bias, etc) by talking to real people outside of an organization.

    See also:


  • Business Minded

    Users that frame decisions by understanding cost, benefits, and want to buy solutions. They often use ROI as a mental model for whether or not to use a product or choose between providers. When choosing a technical solution they value speed of delivery and ongoing costs. They are more likely to choose ‘plug and play’ options they don’t have to imagine.

    See also: