Juan Olvera

  • Zero-Sum budgeting with Double-Entry accounting

    Here’s how double-entry accounting creates an effective budgeting system.

    How they fit together

    Zero-sum budgeting (giving every dollar a job) and double-entry accounting (every transaction affects two accounts) work perfectly together because both ensure everything balances to zero. When you gain $100, it must be assigned somewhere. When you spend $50, both your bank account and budget category decrease.

    Core components

    Accounts: Where you money physically lives (checking accounts, credit cards).

    Categories: What you plan to use your money for (groceries, rent, fun money).

    Transactions: Money moving between accounts and categories.

    Account types

    Traditional accounting has five account types, but for budgeting we focus on three:

    • Assets (your bank accounts): Increase with credits, decrease with debits.
    • Liabilities (your credit cards): Increase with credits, decrease with debigs.
    • Equity (your budget categories AND income): Increase with credits, decrease with debits.

    Why Categories Are Equity, Not Expenses

    In traditional accounting, groceries would be an expense account. But in budgeting, your “Groceries” category isn’t tracking what you’ve spent – it’s tracking what you can spend. It represents a portion of your wealth assigned to a specific purpose. That’s why categories work like equity accounts.

    Why Income Is Equity, Not Revenue

    Your Income category works as an equity account rather than a revenue account because it temporarily holds unallocated funds until you distribute them to categories.

    Income functions as “unassigned equity” while budget categories represent “assigned equity.” Budgeting is simply the process of moving equity from unassigned to assigned status.

    How Transactions Work

    Getting paid:

      AccountTypeActionAmount
      CheckingAssetDebit (+)$1000
      IncomeEquityCredit (+)$1000

      Budgeting money:

        AccountTypeActionAmount
        IncomeEquityDebit (-)$200
        GroceriesEquityCredit (+)$200

        Spending money:

          AccountTypeActionAmount
          GroceriesEquityDebit (-)$50
          CheckingAssetCredit (-)$50

          The Benefits

          This approach provides accurate accounting: everything maintains balance, errors are identifiable, and you can track both the location of your money (accounts) and its purpose (categories).

          Zero-sum budgeting powered by double-entry accounting gives you a complete system for managing your personal finances effectively.

        1. What I cannot create, I do not understand

          I saw a post in Hacker News about Richard Feynman’s blackboard at the time of his death. The first comment mentions his motto: “What I cannot create, I do not understand.”

          This motto matches the Not Invented Here syndrome and how I’ve been learning programming.

          When I first started to get serious about Frontend development, there was Backbone.js. This amazing library abstracted concepts into an easy-to-use API that allowed you to build powerful Frontends.

          However, before I started using it, I had an urge to understand it, so I wrote my own version. After I nailed down the concepts, I was comfortable using Backbone.js.

          This post is a glorified bookmark with notes reminding me how I started learning these things.

        2. Quick guide to convert a Python script into a CLI tool

          Reading the Click’s documentation will bring you up to speed on what it can do, but I’m writing a quick list of steps I follow every time I need to make a quick CLI out of a Python script.

          So, let’s start by creating a Python project and installing Click as a dependency.

          $ poetry new cool-tool
          $ cd cool-tool
          $ poetry add click

          Poetry will create a scaffold directory that looks like this:

          cool-tool/
            cool_tool/
              __init__.py
            poetry.lock
            pyproject.toml

          For this example, we will create a main.py file with a very cool function.

          $ cd cool-tool 
          $ touch cool_tool/main.py

          In our file we create a function and some logic.

          # main.py
          def cool():
            print("hello cool world")

          OK, now to the part we really care. Let’s convert this script into a CLI

          • create a directory named “console” inside our main module.
          • create a __init__.py file to let Python know this is a module.
          • create a run.py file where we will add our Click logic.
          $ mkdir console
          $ touch console/__init__.py
          $ touch console/run.py

          Inside our run.py we add:

          import os
          import click
          from cool_tool.main import cool
          
          @click.group
          def cli():
             pass
          
          
          @click.command
          @click.argument("cool")
          def show_cool():
             click.echo(cool())

          Then we add our CLI script to our pyproject.toml so we can use it directly inside our virtual environment, e.g., cool --help. Add this section and we should be done:

          # pyproject.toml
          [tool.poetry.scripts]
          cool = 'cool_tool.console.run:cli'

          Run poetry install, make sure you have the virtual environment activated, and verify everything works by running the help command.

          $ poetry install
          $ poetry shell
          $ cool --help
          
          Usage: cool [OPTIONS] COMMAND [ARGS]...
          
          Options:
            --help Show this message and exit.
          
          Commands:
            show_cool

          Resources

        3. Python function to send a single Telegram bot message

          This is my console.log equivalent when I need a quick observability implementation.

          Usually, when you build a Telegram bot, you have to have a small server listening for updates.

          The server logic is abstracted away as a simple API using the python-telegram-bot package, but we can write a simpler implementation for a single message:

          import os
          import telegram
          
          TELEGRAM_BOT_ID = os.getenv("TELEGRAM_BOT_ID")
          TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
          
          async def send_telegra_notification(msg: str, origin: str = "AppName:main"):
              notification_msg = f"*{origin}*: {msg}"
              bot = telegram.Bot(token=TELEGRAM_BOT_TOKEN)
              try:
                  await bot.initialize()
                  await bot.send_message(
                      chat_id=TELEGRAM_BOT_ID,
                      text=notification_msg,
                      parse_mode=telegram.constants.ParseMode.MARKDOWN,
                  )
              except Exception as e:
                  # handle exception, e.g., logger.error(e)
                  print(e)
              finally:
                  await bot.shutdown()
          
        4. Deploy PostgREST to Dokku

          Here is a list of steps to deploy a PostgREST instance to Dokku.

          Create a new app.

          dokku apps:create pgrest-myproject

          Add a port mapping from 3000 (PostgREST container) to 80 (host).

          dokku ports:add pgrest-myproject http:80:3000

          Create a new PostgreSQL database using the dokku-postgres plugin and link it to our project.

          dokku postgres:create pgrest-myproject
          dokku posgres:link pgrest-myproject pgrest-myproject

          A new environment variable will be created:

          =====> pgrest-myproject
          DATABASE_URL: postgres://postgres:alongpass@dokku-postgres-pgrest-myproject:5432/pgrest_myproject

          But we need to define the same value in a variable named PGRST_DB_URI so PostgREST can access the database.

          dokku config:set pgrest-myproject PGRST_DB_URI="postgres://postgres:alongpass@dokku-postgres-pgrest-myproject:5432/pgrest_myproject"

          Pull and deploy the Docker image from dockerhub.

          docker pull postgrest/postgrest:latest
          docker tag postgrest/postgrest:latest dokku/postgrest:latest
          dokku git:from-image pgrest-myproject dokku/postgrest:latest

          Add a Let’s Encrypt TLS certificate with the Dokku letsencrypt plugin.

          dokku letsencrypt:set pgrest-myproject email <your-email>
          dokku letsencrypt:enable pgrest-myproject
        5. Launch a simple HTTP Server in One Line of Python

          I always google this snippet when I need it, so I will put it here for easier access.

          python3 -m http.server 8080 -d ./public
        6. Types of UUID

          • v1: mac address + time + random
          • v4: completely random
          • v5: input + seed (consistent, derived from input)
          • v7: time + random (distributed sortable ids)

          Source

        7. Ubuntu Desktop personal setup

          This is how I customize a clean Ubuntu install.

          Essentials

          First we install the essentials.

          sudo apt install curl vim fish tmux

          SSH keys

          ssh-keygen -t ed25519 -C "user@domain.com"

          Copy public key to paste as needed:

          xclip -sel clip < ~/.ssh/id_ed25519.pub

          Folder structure

          Update the folder structure. Basically changing the defaults locations of folders like ~/Downloads and ~/Desktop.

          Create new folders.

          cd ~
          mkdir work dl docs media tpl pub \
            media/{music,books,pics,vids,screens,torrents}

          Make Ubuntu use our new folders. Edit ~/.config/user-dirs.dir and update the file so it looks like this:

          XDG_DESKTOP_DIR="$HOME/desk"
          XDG_DOWNLOADS_DIR="$HOME/dl"
          XDG_TEMPLATES_DIR="$HOME/tpl"
          XDG_PUBLICSHARE_DIR="$HOME/pub"
          XDG_DOCUMENTS_DIR="$HOME/docs"
          XDG_MUSIC_DIR="$HOME/media/music"
          XDG_PICTURES_DIR="$HOME/media/pics"
          XDG_VIDEOS_DIR="$HOME/media/vids"

          Fish configuration

          Make fish the default shell

          Install Fisher.

          Install z:

          fisher install jethrokuan/z

          Ubuntu configuration

          Add Spanish:

          • Settings
            • Region & Language
            • Input Sources
            • Add Spanish
          • Appearance
            • Dark
            • Dock
              • Auto-hide the Dock: On
              • Position on screen: Bottom

          Install GNOME tweaks

          sudo apt install gnome-tweaks

          With GNOME Tweaks activate:

          • Top Bar
            • Activities Overview Hot Corner
            • Battery Percentage
            • Weekday
          • Workspaces
            • Static Workspaces
              • Number of Workspaces: 5
              • Workspaces span displays

          GNOME Terminal configuration

          Download and install the Dracula theme for GNOME Terminal.

          Restart your terminal to reflect the changes.

          Tmux configuration

          Update ./config/tmux/tmux.conf:

          set -g @plugin 'tmux-plugins/tpm'
          set -g @plugin 'dracula/tmux'
          set -g @dracula-plugins 'git, network-ping, attached-clients, network-vpn, weather, time'
          set -g @dracula-show-left-icon 'uwu'
          
          run -b '~/.tmux/plugins/tpm/tpm'

          Install Node

          Install n:

          curl -L https://git.io/n-install | bash

          Install Python

          Install pyenv using pyenv-installer.

          Be sure to install the build environment dependencies:

          • https://github.com/pyenv/pyenv/wiki#suggested-build-environment

          Install poetry.

        8. Git commit messages

          Each commit message consists of a header, a body, and a footer.

          <type>(<scope>): <subject>
          <BLANK LINE>
          <body>
          <BLANK LINE>
          <footer>

          Header

          The header is mandatory and the scope is optional.

          Type

          Must be one of the following:

          • feat: introduces a new feature to the codebase
          • fix: patches a bug in your codebase
          • refactor: improves the code without changing functionality
          • BREAKING CHANGE: major version

          Additional types:

          • build: a change that affects the build system or external dependencies
          • chore: same as build but chore is preferred, also any change that doesn’t fit in any other type
          • ci: changes to the CI configuration
          • docs: changes to the documentation
          • style: white-space, formatting, semi-colons, etc.
          • perf: performance improvements
          • test: adding or fixing

          Scope

          The scope can be empty. Scope can be any of these:

          • category, part or section of the codebase we are affecting, e.g.: api, client, tooling
          • a specific package, library or dependency,e.g.: moment.js, requests
          • a ticket number in case the current commit doesn’t close it, e.g.: chore(123): update dependencies

          Subject

          Short (50 chars or less) summary of the change:

          • use the imperative, present tense: “change” not “changed”
          • don’t capitalize the first letter
          • no dot (.) at the end

          Footer

          The footer should contain a closing reference to an issue if any and breaking changes.

          Closes #123, #345
          
          BREAKING CHANGE:
          
          IE6 not supported anymore.

          Semantic versioning

          Conventional commits are designed to work with Semantic versioning.

          • feat: minor version
          • fix: patch version
          • BREAKING CHANGE: major version

          Examples

          Add a new feature without breaking existing functionality. This bumps a minor version, e.g.: 1.2.0 to 1.3.0.

          feat: add search for transactions
          
          Add new React component for searching transactions with autocompletion.
          
          Include tests.
          
          Closes #123, #234

          Fix a bug. This bumps a patch version, e.g.: 1.3.0 to 1.3.1.

          bug(api): add correct status code for existing resources
          
          We return `409` when trying to create a user with an existing email.
          
          Closes #453

          Add new feature that breaks existing functionality. This bumps a major version, e.g.: 1.3.1 to 2.0.0.

          feat!: add bank sync 
          
          This replace CSV import and use Plaid to connect to people's banks.
          
          Closes #423, #424, #425
          
          BREAKING CHANGE:
          
          People won't be able to import transactions using CSV. Only Bank 
          Sync is available.

          Resources

        9. How to resize a disk on Ubuntu server 20.04

          I had an issue with my Thinkpad x220; when I installed Ubuntu, I had only 100GB available out of 1000 GB. So it ran out of available space quickly.

          To resize to all the space available, I ran these commands:

          lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
          
          resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv

          Sources: