23 Commits

Author SHA1 Message Date
8f2ad91e38 Remove Social Icons
All checks were successful
GitHub Pages / deploy (push) Successful in 10s
2026-02-28 08:12:57 -06:00
cf467ff629 Fixup resume
All checks were successful
GitHub Pages / deploy (push) Successful in 13s
2026-02-28 08:07:28 -06:00
e34aea7194 Merge pull request 'Fix edit link' (#1) from dustin-gundrum/blog:fix-link into main
Reviewed-on: #1
2026-02-28 01:01:55 +00:00
a52a95c6bd Update config.yml 2026-02-27 13:37:27 +00:00
fc5afd8668 set the path to blog.paulmontag.info
All checks were successful
GitHub Pages / deploy (push) Successful in 13s
2026-02-25 22:41:36 -06:00
d59033d4f6 more nonsene
All checks were successful
GitHub Pages / deploy (push) Successful in 11s
2026-02-24 22:02:45 -06:00
a3333adcb0 reference a tag
Some checks failed
GitHub Pages / deploy (push) Failing after 9s
2026-02-24 22:00:39 -06:00
4801fe754e something
All checks were successful
GitHub Pages / deploy (push) Successful in 18s
2026-02-24 21:54:52 -06:00
1b151fd27f lets try this
Some checks failed
GitHub Pages / deploy (push) Failing after 10s
2026-02-24 21:50:47 -06:00
e33face849 current directory
Some checks failed
GitHub Pages / deploy (push) Failing after 10s
2026-02-24 21:46:50 -06:00
e4818e76e6 something
Some checks failed
GitHub Pages / deploy (push) Failing after 1m17s
2026-02-24 21:40:00 -06:00
bfe0f324fc do the thing
All checks were successful
GitHub Pages / deploy (push) Successful in 7s
2026-02-24 21:34:32 -06:00
4ab2c05be5 build thing
Some checks failed
GitHub Pages / deploy (push) Failing after 27s
2026-02-24 21:32:24 -06:00
c919d375a8 Added blog for golang package structure
Some checks failed
Deploy Hugo site to Pages / build (push) Failing after 1m47s
Deploy Hugo site to Pages / deploy (push) Has been skipped
2025-11-10 08:58:18 -06:00
39f7d4e3fc Add new image of me 2025-07-28 23:49:22 -05:00
4a9da2db37 More changes 2025-07-28 23:31:58 -05:00
ac5f8bc1ca I bet this doesn't work 2025-07-28 23:03:50 -05:00
1fcb3dfdf8 Grab the latest for the theme 2025-07-28 22:50:59 -05:00
9bb5afb01e Now we see if AI can do the thing I don't want to 2025-07-28 22:47:17 -05:00
df2d2dd904 Added resume to the list 2025-07-28 22:39:57 -05:00
610d9553d4 fixed False 2024-10-07 07:05:13 -05:00
8fa0febdf9 Added an about page 2024-10-07 07:03:32 -05:00
62fa060fdf minor updates 2024-10-07 06:38:09 -05:00
12 changed files with 262 additions and 19 deletions

View File

@@ -0,0 +1,33 @@
name: GitHub Pages
on:
push:
tags:
- '*'
jobs:
deploy:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
submodules: true # Fetch Hugo themes (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: '0.156.0'
# extended: true
- name: Build
run: hugo --minify --destination ./public
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
push: true
tags: "docker.paulmontag.info/blog:${{ github.ref_name }}"

View File

@@ -1,4 +1,3 @@
# Sample workflow for building and deploying a Hugo site to GitHub Pages
name: Deploy Hugo site to Pages
on:
@@ -25,6 +24,7 @@ concurrency:
# Default to bash
defaults:
run:
# GitHub-hosted runners automatically enable `set -eo pipefail` for Bash shells.
shell: bash
jobs:
@@ -32,14 +32,21 @@ jobs:
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.121.0
DART_SASS_VERSION: 1.89.2
HUGO_VERSION: 0.148.0
HUGO_ENVIRONMENT: production
TZ: America/Los_Angeles
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb
sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Install Dart Sass
run: sudo snap install dart-sass
run: |
wget -O ${{ runner.temp }}/dart-sass.tar.gz https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz
tar -xf ${{ runner.temp }}/dart-sass.tar.gz --directory ${{ runner.temp }}
mv ${{ runner.temp }}/dart-sass/ /usr/local/bin
echo "/usr/local/bin/dart-sass" >> $GITHUB_PATH
- name: Checkout
uses: actions/checkout@v4
with:
@@ -47,21 +54,36 @@ jobs:
fetch-depth: 0
- name: Setup Pages
id: pages
uses: actions/configure-pages@v4
uses: actions/configure-pages@v5
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Cache Restore
id: cache-restore
uses: actions/cache/restore@v4
with:
path: |
${{ runner.temp }}/hugo_cache
key: hugo-${{ github.run_id }}
restore-keys:
hugo-
- name: Configure Git
run: git config core.quotepath false
- name: Build with Hugo
env:
# For maximum backward compatibility with Hugo modules
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: |
hugo \
--gc \
--minify \
--baseURL "${{ steps.pages.outputs.base_url }}/"
--baseURL "${{ steps.pages.outputs.base_url }}/" \
--cacheDir "${{ runner.temp }}/hugo_cache"
- name: Cache Save
id: cache-save
uses: actions/cache/save@v4
with:
path: |
${{ runner.temp }}/hugo_cache
key: ${{ steps.cache-restore.outputs.cache-primary-key }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
uses: actions/upload-pages-artifact@v3
with:
path: ./public
@@ -75,4 +97,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v3
uses: actions/deploy-pages@v4

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/public

3
Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM nginx
COPY ./public /usr/share/nginx/html

View File

@@ -1,6 +1,7 @@
baseURL: "https://d1ngd0.github.io/blog/"
baseURL: "https://blog.paulmontag.info/"
title: Paul Montag
paginate: 5
pagination:
pagerSize: 5
theme: PaperMod
enableRobotsTXT: true
@@ -74,7 +75,7 @@ params:
Title: "Hi there \U0001F44B"
Content: Welcome to my blog
# socialIcons:
socialIcons: []
# - name: x
# url: "https://x.com/"
# - name: stackoverflow
@@ -96,7 +97,7 @@ params:
hiddenInSingle: true # hide on single page
editPost:
URL: "https://github.com/d1ngd0/blog/content"
URL: "https://git.paulmontag.info/paul-montag/blog/src/branch/main/content"
Text: "Suggest Changes" # edit text
appendFilePath: true # to append file path to Edit link
@@ -117,6 +118,10 @@ menu:
name: about
url: /about/
weight: 10
- identifier: resume
name: resume
url: /resume/
weight: 20
# Read: https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs#using-hugos-syntax-highlighter-chroma
pygmentsUseClasses: true
@@ -128,3 +133,6 @@ markup:
# guessSyntax: true
# lineNos: true
# style: monokai
caches:
images:
dir: :cacheDir/images

10
content/about.md Normal file
View File

@@ -0,0 +1,10 @@
+++
title = 'About'
date = 2024-10-07T06:53:00-05:00
+++
Whoa, you came to the about page?! Little creepy to want to know so much about me no? Anyway, I am a Father (one daughter), husband (one wife) and Software Engineer at a major retailer. I work on an observability team, providing the tools and infrastructure necessary for other engineers to understand the health of their applications. I also get up at crazy early or uncomfortably late times to dive into topics of interest, write blog posts and do a bit of light reading. Also, never totally mastered proper spelling, and lean quite heavily on spell check. If you see any mistakes, grammar or otherwise, let me know and I'll fix it; maybe.
During my day job I like to solve problems by automating them away. If something has to be done more than once I have a bash script in my `~/.bin/` directory, usually with some help text, to get it done quickly. In other words awk and the `|` have treated me well. If the problem is more complex I will create the tools needed (Often with Go, Rust or something else) to solve it in the most simple yet generic way. I love taking on big novel challenges that require creativity or those annoying problems that no one could quite figure out.
When I am not working I enjoy a plethora of circulating hobbies such as hiking, drumming, painting, drawing, photography, woodworking, cooking, barbecuing, running, writing and more software engineering. That isn't to say I am good at all these things, but I do enjoy them.

View File

@@ -1,6 +1,6 @@
+++
title = 'Grabbing a Go Time with BPFTrace'
date = 2024-10-06T08:45:05-06:00
date = 2024-10-07T06:22:18-05:00
draft = false
+++

View File

@@ -0,0 +1,93 @@
+++
title = 'Golang Package Structure'
date = 2025-11-10T00:00:00-05:00
draft = false
+++
I have been developing with Golang for about 10 years now, and over that time I have learned many things about the language, but the biggest one might be this.
**Directories are not just Directories**
Within golang a directory with new golang source code in it creates a new interface. One that requires you to think through what is public and what is private. One that forces you to consider the relationship between these packages since golang does not allow for [circular dependencies](https://groups.google.com/g/golang-nuts/c/8nwGtohyVtc/m/bFkZUV4X6gwJ). You need to strike a balance. I tend to see the following in codebases
1. Tiny Packages: Often separated by types (`routes/`, `config/`, `utils/`) that are so small almost everything in them is `Public`. This pattern leads to circular dependencies. (I once saw a `utils2/` to solve a circular dependency problem on `utils/`)
2. One Massive package: Often `main` where everything is. Here the author will run into name collisions. Things in `main` also can't be pulled in as a library.
In both of these the biggest issue is not benefiting from what packages are supposed to do: Abstract away complexity! When a package is so tiny everything is `Public` it doesn't become the "Black Box" of power, solving some problem in a "elegant" way higher up. Additionally, throwing everything in main does the same thing. So what do you do.
## The Solution
I have heard the following quote, though I can't find it anymore. I am going to attribute it to someone who likely said it, I'm sure chatGPT will start attributing it to them thanks to me.
"Packages should define scope, not types" - Rob Pike ... probably.
In other words, your packages should be tools, not containers. Think of all the amazing libraries you use and love, they are often all in one big directory; that's because those directories are tools for developers to use to solve a generic problem. Here are some guidelines I set for myself when writing go.
### Put it in Main
Start by putting your logic in main. I have seen codebases bloat to 3x their size when all they are doing is serving a crappy HTTP API. Most likely you don't need some complex pattern, or crazy over abstracted interface setup to make code good. Don't let java fool you into thinking verbosity == professionalism. Readable code is maintainable code.
### Underscore types
I tend to organize my types with a simple nomenclature for my file names: `[interface]_[struct].go`. These are guidelines, not strict rules, there are always exceptions.
#### Each Interface with multiple implementations gets its own file.
The following interface I would put into a file called `runner.go`.
```go
// Runner is an example interface to help you see what I mean, I am adding
// a comment to make the point that you should always add comments to any interface
// since you are creating the interface for future authors to create their own
// types which implement it right? You aren't? Why are you creating an interface
// then?
type Runner interface {
// run is a method which should also have a comment specifying what the caller
// expects out of the method, Is it looking for any specific error types, what
// does it do in certain situations. Spell it out for future you so they don't
// have to go looking through code.
func run(name string) error
}
// RunnerFn is a helper type to create a runner from a function.
// yeah any ancillary or helper functions for this type can go in
// this file too
type RunnerFn func(name string) error
func (rf RunnerFn) run(name string) {
rf(name)
}
```
As you will notice, if you read the comments, we can put not just the interface, but other helper functions in this file too. This makes it easy to quickly find those functions which you know work with that interface type.
#### Structs built as different implementations of an interface get their own file
The following would be in `runner_helloworld.go`
```go
import "fmt"
// HelloWorldRunner is a runner that outputs hello world and the name
// supplied to the runner
type HelloWorldRunner struct {}
func (hw *HelloWorldRunner) run(name string) {
fmt.Println("hello world and " + name)
}
```
Now you have organization by type. Everything that is a `runner/` or `route/` etc gets "organized" by type, while not breaking the namespaces created through packages.
### Could it be a standalone repo?
I ask myself this question before creating a new package. Could I justify putting a `go.mod` file into the directory I am making, and push it to a new repository? would it be useful? If the answer is no I don't create a new directory. The point of packages is to abstract away complexity. If I can't envision the interface which is doing the abstracting, then it's to interconnected to justify putting it in it's own package.
## Conclusion
To be clear, I kinda hate that golang makes me do these things. Languages like rust, which allow multiple layers of public/private declaration solve these problems, though it is far more complex. Golang's mentality is simplicity, but that simplicity means we have to do things a bit different to solve problems with the language in a maintainable way. So:
- Stick with main until you see a clear problem which needs to be abstracted into a simple interface.
- Aim for building tools with your packages, not containing types.
- Utilize a nomenclature for file names to organize things instead of reaching for directories.
- Justify every package abstraction layer you add by asking "could this be a standalone library?"

66
content/resume.md Normal file
View File

@@ -0,0 +1,66 @@
+++
title = 'Resume'
date = 2025-07-28T17:28:17-05:00
draft = false
+++
# Paul Montag - Software Engineer
[https://github.com/d1ngd0](https://github.com/d1ngd0) :: [https://blog.paulmontag.info/](https://blog.paulmontag.info/)
Lead Software engineer, specializing in golang development, server administration and observability.
<!-- TODO: Add stuff about how you are a good leader, how you automate simple things with bash and complicated things with well thought out software. -->
## Skills
Languages: Golang, Rust, bash, javascript, HTML, CSS, YAML, JSON, python, awk
Tools: Ansible, git, github, Kubernetes, KubeBuilder, Argocd, Helm, kafka, elasticsearch, Clickhouse, influxdb, vim, helix, delve, chef, mercurial, Terraform, OpenTelemetry, Grafana, Postgres, MySQL/MariaDB, ChatGPT, Copilot, Burrow, BPFTrace, Linux, Ubuntu, CentOS, GCP, AWS
## Experience
### Target - Minneapolis, MN
#### Lead Engineer 2019 - present
- Manage replicated dataset with Petabytes of data, and billions of transactions daily
- Manage highly resilient, zonal aware Clickhouse architecture in Kubernetes
- Maintain Elasticsearch, Influx, Grafana architectures on VMware with ansible
- Lead small team of engineers through various initiatives utilizing Agile methodology
- Work with engineers throughout Target to better understand observability tooling
- Present talks within Target to create a culture of observability, and desired engineering practices
- Create highly performant, specialized applications in golang to manage observability data
### ISeeMe - Eden Prairie, MN
#### Software Engineering Manager 2016 - 2019
- Part of leadership team at ISeeMe to define year and quarterly rocks
- Lead small team of engineers in completing rocks
- Managed AWS billing
- Lead Agile ceremonies
- Maintained responsibilities from software engineer position
#### Software Engineer 2015 - 2016
- Develop new features in PHP and Golang for personalized books
- Maintain Linux (Ubuntu) servers for running personalization software
- Maintain Magento web store
- Maintain Kubernetes cluster utilizing AWS and bare metal (at printing locations)
### Meta 13 Interactive - St Cloud, MN
#### Web Programmer 2012 2015
- Designed database structures using MSSQL and MySQL
- Used existing frameworks to create applications for clients
- Created custom frameworks using object oriented and procedural PHP
- Used source control software such as Git and Mercurial
- Communicated with clients to plan applications and discuss updates and regular maintenance
- Created HTML and CSS using custom code as well as frameworks like Bootstrap
- Worked both individually and with a team of developers to complete projects
- Configured Apache, MySQL, and PHP on Linux servers as well as MSSQL and IIS on Windows servers
## Education
### Saint Cloud Technical College
Saint Cloud, MN 2008 2010
- Associate of Applied Science Degree in Computer Programming

7
cspell.json Normal file
View File

@@ -0,0 +1,7 @@
{
"userWords": [
"Montag",
"Clickhouse",
"Magento"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 158 KiB