• Four Kinds of Luck

    There are four kinds of luck.

    The simplest form is extrinsic, something fortunate happens to youβ€”we tend to call that blind luck.

    Then there is luck that happens by increasing the number of chances to be luckyβ€”we often see that in people who output an enormous amount and are in constant motion.

    The next is luck from being skilled in a particular area and spotting lucky breaks that others don’t noticeβ€”this is prepared luck.

    Lastly, there is luck that is drawn to you from being in a position where other’s luck becomes your luckβ€”this is due to your unique characteristics, second-order luck. This is the hardest to understand, but consider the example of a deep sea diver who is the best in the world. When a sunken ship filled with treasure is discovered deep underwater, this opportunity will go to them because they are known to be the best.

    See also:


  • Habit Stacking

    Using an established habit as a cue for initiating another habit. For example, using the fact that you brush your teeth every night and build on it by flossing too. This makes it more likely that you will perform the action and establish a habit.

    It also works both waysβ€”not having a consistent cue to stack on top of means you will be less likely to remember to do the second habit.

    See also:


  • Workaround for Mapping AWS Cognito Identity ID to Username

    There is a longstanding issue where there is no way to go from an AWS Cognito identity ID to a username. The workaround I found is complicated, but should work without posing a security risk (e.g. client setting the username must not be relied on otherwise they could hijack someone’s account). Using Cognito credentials (authenticated), make a call to a Lambda from the client app with an identity ID and ID token. In the Lambda compare the identity ID to the context’s identity ID, verify the ID token, then set a mapping in S3.

    As a further optimization, rather than performing this operation every time the client app is loaded, use a custom attribute in the Cognito User Pool to set a field when the mapping is performed. The client can then check the user attribute on load and skip the operation if has already been performed.

    import * as AWS from 'aws-sdk/global';
    import S3 from 'aws-sdk/clients/s3';
    import CognitoIdentity from 'aws-sdk/clients/cognitoidentity';
    import jwt from 'jsonwebtoken';
    import jwkToPem from 'jwk-to-pem';
    import fetch from 'node-fetch';
    
    const env = process.env;
    const AWS_S3_BUCKET = env.AWS_S3_BUCKET as string;
    const AWS_COGNITO_USER_POOL_URL = env.AWS_COGNITO_USER_POOL_URL as string;
    const AWS_COGNITO_USER_POOL_CLIENT_ID = env.AWS_COGNITO_USER_POOL_CLIENT_ID as string;
    
    
    export const handler = async (event: any, context: any) => {
      const context_identity_id = context.identity.cognitoIdentityId;
      const request_token = event.idToken;
      const request_identity_id = event.identityId;
    
      if (context_identity_id !== request_identity_id) {
        console.error('Identity ID not verified', { context_identity_id, request_identity_id });
        return;
      }
    
      // Verify the JWT to make sure it is valid
      // https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
    
      // WARNING: This is not verified, we need to
      // get some info from the token to verify it
      const decoded_jwt: any = jwt.decode(request_token, { complete: true })!;
    
      if (decoded_jwt.payload.aud !== AWS_COGNITO_USER_POOL_CLIENT_ID) {
        throw new Error(`JWT token has invalid audience: got ${decoded_jwt.payload.aud} expected ${AWS_COGNITO_USER_POOL_CLIENT_ID}`);
      }
    
      if (decoded_jwt.payload.iss !== AWS_COGNITO_USER_POOL_URL) {
        throw new Error(`JWT token has invalid issuer: got ${decoded_jwt.payload.iss} expected ${AWS_COGNITO_USER_POOL_URL}`);
      }
    
      // Fetch the key used to sign the jwt
      const response = await fetch(`${ AWS_COGNITO_USER_POOL_URL }/.well-known/jwks.json`,  { method: 'GET' });
      const jwk = await response.json();
      const key = jwk.keys.find((key: any) => {
        return key.kid === decoded_jwt.header.kid
      });
      const pem = jwkToPem(key);
    
      jwt.verify(request_token, pem, function (err: any, decoded: any) {
        if (err) {
          throw new Error(err);
        }
      });
    
      // Now we can trust the claims of the jwt
      const username = decoded_jwt['cognito:username'];
    
      // Write the mapping to S3
    
      const s3 = new S3();
    
      // Add a mapping from identity ID to username
      const identity_id_to_username_key = `app/identity_id_to_username/${context_identity_id}/${username}`;
    
      // This S3 location should never be writeable from the client app
      // otherwise they could hijack someone's account by setting someone
      // else's username!
      await s3.putObject({
        Bucket: AWS_S3_BUCKET,
        Key: identity_id_to_username_key,
        // There is no need to put a body in the object since all the
        // information is encoded into the key.
        Body: undefined
      }).promise();
    
      // Add a mapping from username to identity ID
      // Not sure if this will be needed, but would be annoying to find
      // out later and write a migration.
      const username_to_identity_id_key = `app/username_to_identity_id/${username}/${context_identity_id}`;
    
      await s3.putObject({
        Bucket: AWS_S3_BUCKET,
        Key: username_to_identity_id_key,
        Body: undefined
      }).promise();
    };
    

  • Truncating Text Is Complicated

    To truncate text in a way that looks good and retains substances is surprisingly complicated. I started writing my own after being frustrated with the built-in truncate method in Hugo.

    A good text truncation method has the following requirements:

    • Maximizes the amount of characters to the target limit
    • Minimizes breaking paragraphs, sentences, lists, and words
    • Appends ellipses if the input does not fit within the target limit
    • Accounts for the addition of ellipses in the target limit
    • Ignores the title/heading if it’s also in the input text

    As a simple example, given this input text:

    > Quisque efficitur nunc sit amet lacus blandit, quis condimentum magna sodales vestibulum molestie suscipit libero, id eleifend nisl facilisis sit amet.

    With a truncation limit of 140, the output should be:

    > Quisque efficitur nunc sit amet lacus blandit, quis condimentum magna sodales. Vestibulum molestie suscipit libero, id eleifend nisl . . .


  • No-Code

    A category of products and tools that enable users to create things that previously required developers to do. Examples include business websites (Webflow), chat bots (Landbot), surveys (Typeform), native apps (Thunkable), voice apps, email marketing, etc.

    No-code is a business model similar to Naval Ravikant’s take on making things that were previously only for rich people available to everyone (Uber is just a black car service that anyone can have).


  • Venture Capital Has Conflicting Incentives With Founders When It Comes to Outcomes

    Venture capitalists and institutional investors need big returns and therefore they fund companies pursuing a big market (investor returns follow a power law). Fund sizes have increased and investors need larger and larger returns. However, there is a tension with founders' incentives to have a wide range of exit outcomes. An exit in the $100s of millions is life changing for a founder, but not a good enough outcome for investors. Investors are incentivized to push founders to riskier, but bigger opportunities even if the set of possible positive outcomes for the founder are significantly smaller (e.g. a highly competitive winner-takes-all market).

    See also:

    • This is somewhat related to the principal-agent problem, investors do often act on behalf of founders (and even entire markets).

  • Peter Principle

    A theory of management in which employees in a company are promoted until they are at a level of incompetence. For example, you get promoted for doing well at level 1 so you are promoted, but then you never get promoted again.

    See also:


  • WebAssembly

    A compilation target for executing in a virtual machine supported by web browsers and servers (via tools like wasmtime). WebAssembly is intended to be portable between platforms and has a similar ‘write once run anywhere’ ethos as Java. WebAssembly already runs significantly faster than JavaScript in the browser and there is reason to believe the gap will widen as WebAssembly support improves.

    Current limitations include by native access to manipulating the DOM, compiling from garbage collected languages results in huge binaries, and performance (Firefox is the fastest). Rust has the most mature support for compiling to wasm.

    See also:


  • The Gervais Principle

    There are three groups of people within an organization: Sociopaths (tend to be at the top running the company, characterized by self-interest and need to control), Clueless (tend to be middle management, characterized by misplaced loyalty to the organization), and Losers (tend to be at the bottom, characterized by striking a bad economic bargain). The Gervais Principle speaks to the dynamics between these three groups with the Office as allegory.

    Sociopaths (who wield all the power) promote over-performing Losers into middle management thereby making them Clueless. They maneuver the Clueless to escape their own risk. For example, Michael was a top sales person before becoming a Clueless manager.

    Sociopaths turn under-performers into Sociopaths and fast-track them to positions of power. Losers that are self-aware (knowing they made a bad economic bargain) seek opportunities to raise their standing before they run out of time and get fired. For example, Ryan the intern who is bad at sales (his actual job) and manipulates his way into management position by David (a Sociopath).

    The Losers are left to fend for themselves. They find fulfillment outside of work and do just enough to get by. This is exemplified by Stanley, who knowingly plays along to the delusions of the Clueless and does enough work to never get fired.

    Read the essay.

    See also:

    • Performance management is a way of preventing the rise of low-performing Losers into Sociopaths in positions of power
    • Peter Principle a more popular (and simplistic) explanation of organizational incompetence
    • This theory can be thought of as an example of the principal-agent problem, the organization is not properly incentives to counteract the bad behaviors of promoting low-performing Losers and senior managers manipulating the Clueless.

  • TypeScript Adds Incidental Complexity to Using Libraries

    Inter-operating with libraries in the JavaScript ecosystem from TypeScript introduces incidental complexity due to packaging and types. There are (at least) four different ways to import a library[0] in TypeScript, each with subtle differences. Depending on the way the library is packaged you will need to pick the correct incantation.

    To get the benefit of static typing for a non-typed library, you also need to add @types/name-of-lib to your dependencies. Sometimes these are no longer needed when the library adds TypeScript support as did aws-sdk which, depending on what blog post or StackOverflow question you are looking at may or may not tell you.

    Finally, types in a library that has TypeScript support can be done poorly. For example, I found a rather unfortunate issue in marked.js because the Tokens union type doesn’t have a common discriminator property. That makes it not possible, or extremely hack-y to find a specific kind of element[1].

    [0]

    import { Lib } from 'some-lib'
    import 'some-lib';
    import * as Lib from 'some-lib';
    const lib = require('some-lib');
    

    [1]

    export function extractMarkdownTitle(md: string): string | null {
      const lexer_output = marked.lexer(md) as marked.TokensList;
    
      // Hack: This isn't typeable because Tokens is not a descriminating
      // union type (Def and Space don't have a `type` prop)
      let title: any = lexer_output.find((val: any) => {
        // Without casting to `any`, this won't compile
        return val.type === 'heading';
      });
    
      return title ? title.text : null;
    }