# mindiweik: full blog content
> Software engineer, writer, builder. Figuring it out in public, one version at a time.
## tag it and ship it: building a CD pipeline for a startup in alpha
URL: https://mindiweik.com/blog/tag-it-and-ship-it-building-a-cd-pipeline-for-a-startup-in-alpha/
Published: 2026-07-23
When you're a small startup with two engineers and zero users watching, it's tempting to just SSH into the server and run `docker run` manually every time you want to ship something.
We did that for a while. It worked. But it was painful.
This is the story of how we built a real CI/CD pipeline for [Audition Cat](/projects/audition-cat/), a casting and audition platform currently in alpha, and what changed between "the plan" and "the thing that actually runs in production."
## why we needed a pipeline
The manual deploy worked fine, honestly. But it was both painful and potentially very unreliable, with us leaning into hope that copy/paste of commands would work the same each time and have no operator errors.
"Just SSH in" stops being cute when:
- You're often shipping release candidates to staging before they touch prod
- Production deploys need a human to approve before they run
- You want the same process every time, not "where's my copy/paste reference again?"
We also wanted versioning. Real versioning. The kind where you can look at a running container and know exactly what code is in it.
So we sat down and made some decisions.
## the decisions (the plan)
**Git flow + tag-based deployment.** Not branch-based. We push a tag, the pipeline runs. No magic branch names, no manual triggers.
**Semantic versioning.** `vMAJOR.MINOR.PATCH`, e.g. `v1.0.0`. Frontend and backend are versioned independently because they don't always ship together.
**Two tag patterns, two behaviors:**
- `v1.0.0-rc.1` → staging deploy ("release candidate," automatic)
- `v1.0.0` → production deploy (manual approval required)
**Staging and prod are separate environments.** Staging runs on its own EC2 instance. Same RDS cluster, but with a different database (`auditioncat_staging`). Zero code changes needed. We just pass different env vars.
**Docker images go to Docker Hub.** Tagged by version. Every deploy pulls the exact image it needs. No rebuilding on the server.
That was the plan. Clean. Reasonable. Mostly right.
## what actually happened when we built it
### the runner architecture changed completely
The original plan had one runner, `mindi-local` (my Linux machine on a Raspberry Pi). It was handling everything: building Docker images, SSHing into servers, running deploys.
_You can see more about my adventures setting up this GitLab runner:_
gitlab runner - part 1 →
gitlab runner - part 2 →
But...that wasn't enough power or space.
Building multi-platform Docker images from a local machine and SSHing into EC2s across every deploy is slow, brittle, and created a hard dependency on my machine being on and available.
Not great.
What we ended up with instead were dedicated GitLab runners on the EC2 instances themselves.
- `ec2-runner-staging` lives on the staging server. It builds staging images and deploys them locally. No SSH, no remote commands, just `docker run` on the machine it's already on.
- `mindi-local` still handles dev CI pipelines and production deploys.
### disk space is real
Early on, the staging server started choking. Docker images build up fast, and a t2.micro doesn't have a lot of room to breathe.
One fix was adding cleanup steps directly into the pipeline:
```yaml
before_script:
- docker system prune -af || true
- docker builder prune -af || true
after_script:
- docker image prune -f
- docker builder prune -f
```
Not glamorous, though extremely necessary. If you're running Docker on a small instance, build cleanup into your pipeline from day one.
### SSH key encoding tripped us up
The prod backend deploy still runs from `mindi-local` via SSH. When we set up the GitLab CI variable for the SSH key, we stored it base64-encoded (common approach for multiline secrets in CI). Fine, except the deploy script needs to decode it before adding it to the agent:
```yaml
- echo "$EC2_SSH_KEY" | base64 -d | ssh-add -
```
The original draft had `tr -d '\r'` instead of `base64 -d`. The wrong format for how we ended up storing the key. We caught it when the first prod deploy attempt just sat there doing nothing. Always test your SSH setup in a throwaway pipeline first!
### docs get linted too
The backend pipeline picked up a `docs-lint-job` that wasn't in the original plan. We're using TypeDoc for API documentation, and it turns out having a lint step for your docs is actually useful. Broken doc comments fail the pipeline before they get anywhere near a deploy.
## what the pipeline looks like now
**On every merge request:**
`lint → build → test` (including API docs lint on backend)
If any of those fail, the MR doesn't merge. Simple.
**On a release candidate tag (`v1.0.0-rc.1`):**
`docker build (staging) → deploy to staging` (automatic)
Both frontend and backend deploy independently. Staging is up within a few minutes of pushing the tag.
**On a production tag (`v1.0.0`):**
`docker build → deploy to production` (plus manual approval required)
The manual gate is intentional. We're a small team and production deploys should feel deliberate for now, not automatic.
**On a schedule:**
[Renovate](https://docs.renovatebot.com/) runs and opens dependency update MRs automatically. We review them as often as possible.
## what's next
The pipeline is solid but we're still filling in some gaps:
**Changelog automation.** We recently set up an automation to take in our changelog updates. The pipeline scans commits since the last tag, we run it through AI just to come up with more user-friendly descriptions, and then push an entry to our Notion changelog DB automatically on every production deploy.
**Moving the backend prod deploy off mindi-local.** It works, but the dependency on my machine being available is annoying. `ec2-runner-backend` is on the list. I initially set up my local runner to learn something new and for cost efficiency. We could also use existing resources, like EC2 instances already active.
## if you're building something similar
A few things I'd do from the start:
1. **Runners on the servers, not remote SSH.** It's faster, simpler, and you don't need to manage SSH keys across every job.
2. **Add disk cleanup to Docker jobs.** You will run out of space. Build the cleanup in before you need it.
3. **Separate tag patterns for staging vs prod.** The `v*-rc*` / `v*.*.*` split is clean and easy to reason about.
We went from "SSH and pray" to a pipeline we actually trust. It took a few rounds of iteration, but it runs, it's repeatable, and the best part is that neither of us has to remember a deploy checklist anymore!
Are you building CI/CD for a small team or solo project? I'd love to hear what your setup looks like. 🚀
_[Audition Cat](/projects/audition-cat/) is a casting and audition platform currently in alpha. Follow along as we build it in public._
## ai is rubber duck debugging, but the duck talks back
URL: https://mindiweik.com/blog/ai-rubber-duck-debugging/
Published: 2026-07-16
You know rubber duck debugging, right?
The idea is simple: when you're stuck on a problem, explain it out loud to a rubber duck sitting on your desk. The act of articulating the problem, step by step, out loud, forces your brain to organize the chaos. And somewhere in that explanation, you usually find the answer yourself.
It works. Sometimes embarrassingly well.
Lately I've been doing something even better. Same concept, but the duck talks back.
## the setup
I'm a founding engineer at a startup. We're small, moving fast, and I often find myself deep in territory I haven't been in before. CD pipelines. Docker buildx. GitLab CI. Infrastructure stuff that I'm interested in, but isn't my daily bread.
I could Google my way through it. I could read docs. I could post on Stack Overflow and wait three days for someone to tell me my question is a duplicate.
Or I could just... talk through it. With AI.
And I mean _actually_ talk through it, not "write me a CI pipeline," but "here's what I'm trying to do, here's what's happening, here's what I've already tried, what am I missing?"
There's a definitive difference.
## what makes it actually work
The magic isn't AI itself. The magic is **the process of explaining the problem.**
When you're forced to write up what's happening like what you expected, what actually occurred, or what you've tried, something clicks. You start to understand the shape of the problem better just by describing it. Often I figure out the next thing to try _while writing the message._
It's the rubber duck effect. Except when you're done explaining, the duck goes "oh interesting, that error usually means X. Have you checked Y?" and suddenly you're actually learning _why_ X causes that behavior, not just copying a fix from Stack Overflow.
## a real example
Recently I spent a few hours debugging a CD pipeline for that startup project I work on. Here's a taste of what we went through:
- GitLab pipelines not triggering on tags (protected variable scoping, who knew!)
- Docker login failing in CI with a TTY error
- DNS resolution dying inside buildkit containers
- Network connections randomly dropping mid-`npm ci`
- A multi-stage Dockerfile optimization to eliminate the network calls entirely
Each one of those could have been a rabbit hole I fell into alone, probably getting frustrated and copy-pasting fixes I didn't understand.
Instead, each time something broke, I pasted the error and explained what I was seeing. We troubleshot it together. I asked "why does this happen?" and got actual explanations, not just "run this command." By the end, I understood Docker credential stores, buildkit networking, and why multi-stage builds matter in a way I genuinely wouldn't have if I'd just followed a tutorial.
## the writing-up-the-problem part is a skill
This is maybe the most underrated part.
Learning to clearly describe a technical problem is a superpower. It forces you to:
1. **Know what you actually tried** (not just "it didn't work")
2. **Separate what you expected from what happened**
3. **Identify what you don't know** (which is where the learning lives)
When you can do this well, you become better at asking for help from humans too. Your Slack messages are clearer. Your GitHub issues are more useful. Your standups are tighter.
And weirdly, sometimes you don't even need the answer. You just needed to write it out.
## we're all being told to embrace AI anyway
If you work in tech, you've gotten the memo by now: use AI. Your company wants it, your tools ship with it, half the job listings mention it. The push is everywhere.
I'm on board. Not because I was told to be, but because this is the version of AI use that actually makes you better at your job. You're not outsourcing the thinking. You're thinking out loud with a patient, knowledgeable collaborator who doesn't judge you for asking a "basic" question.
That distinction matters, because "embrace AI" can mean two very different things. It can mean pasting in a ticket and shipping whatever comes back. Or it can mean this: explaining, asking why, following up, actually understanding the fix before you apply it. One makes you faster today. The other makes you faster forever.
The learning happens in the conversation. In the follow-up questions. In the "wait, why does that work?" moments.
The rubber duck was always a tool for thinking. This is just taking it to the next level.
_If you've been using AI as a learning tool (or you're still figuring out where it fits in your workflow), I'd love to hear about it. Find me on LinkedIn._ 💙
## the job search, honestly
URL: https://mindiweik.com/blog/the-job-search-honestly/
Published: 2026-07-09
I applied to 65 jobs. At my peak I had 3 active processes going at the same time. I made it to final rounds with 3 companies and all the way to the very end with 2. I got 1 offer. The other finalist liked me enough that even after the process ended, they wanted to put me in for a different role. And the third? A change in hiring plans cut that process short, but they invited me to come back later.
And for most of those interviews? I thought I was doing between mediocre to terrible.
That contrast, the numbers versus how it felt from the inside, is the whole story. So let's actually tell it.
## the setup
Leaving my last job was the right call, not an escape hatch. Those are different things, and knowing which one you're doing matters for everything that comes after.
I was targeting TypeScript and Node roles, growth and product-leaning teams, remote or Boulder/Denver hybrid. The search started focused, got broader when the silence stretched out, then narrowed again as I learned what I actually wanted. First application to final rounds took about eight weeks.
I also logged everything. Every application, every rejection, every recruiter email, the moment it happened (AI did the bookkeeping; my memory did not get a vote). That discipline is the only reason the numbers in the first paragraph are exact and not vibes.
## what it actually felt like
**You are your own worst critic.** Out of all those interviews, maybe one or two actually felt like they went well. The rest ranged from "fine, I guess" to "I feel like I just bombed that." And yet: three final rounds, one offer, and two companies that wanted to keep the door open.
The internal scorecard is not a reliable instrument. Your read on your own performance is shaped by nerves, recency bias, and the one question you fumbled, not the whole picture. The interviewers are seeing something you're not. If you take one thing from this post, take that.
**The bar shifts in ways nobody warns you about.** As an experienced engineer, it's not just "can you solve this problem." It's: can you articulate every decision you've ever made and why? Can you hold ambiguity without freezing? Do you actually know what you know, or do you just know how to execute?
The system design rounds especially. You have to think out loud in a way that feels pretty unnatural, and the first few times it feels like performing expertise you're not sure you have. That's a skill you build by doing it badly a few times. Ask me how I know.
**The network made it survivable.** No lonely montage here, the opposite actually. I reached out to a lot of people. Former colleagues, community connections, people I barely knew. Most of them responded. Some of them opened doors directly. The job search is not a solo sport, and reaching out kept the momentum going during the stretches of silence.
There will always be some silence.
## what it revealed
Here is the part few post about: the waiting.
The gap between interviews, between rounds, between the "we'll be in touch" and the actual touch. That's where the self-doubt lives. Not in the hard technical question. In the quiet Tuesday afternoon when you've heard nothing from anyone for four+ days. The antidote, for me, was keeping the pipeline moving so silence from one place couldn't consume all my mental space. Having the whole pipeline logged in one place helped more than I expected. When a company went quiet, I could see at a glance that the silence was one row in a table, not the whole story.
A few other things the search taught me, mostly by force:
1. **Dealbreakers are worth knowing early.** You don't always know your real ones until you're in a room and something just doesn't land. That feeling is data. Trusting it earlier saves everyone time.
2. **Culture fit is not soft.** It's load-bearing. Technical fit AND the gut read on the people both have to be right, and you can tell surprisingly quickly when it's there and when it isn't.
3. **What you actually want clarifies fast.** The first few processes helped define what mattered. By the end it was much cleaner: the kind of work, the kind of team, the kind of problem. Logging everything from the start helped here too. The pattern of which conversations I left energized and which ones I dreaded was sitting right there in the record, telling me what I wanted before I'd admitted it to myself. Starting with that clarity would have made the early weeks faster.
## the close
The chapter is closed. Something new has already started, and it's good.
I won't pretend the search was fun, because it wasn't. But it was clarifying in a way I didn't expect. I know myself better as an engineer than I did before it. I know what I'm worth. I know what I'm looking for. That knowledge cost 65 applications, and I'd still call it a fair price.
If you'd rather hear this story than read it, I recently unpacked the whole thing with [Anna Miller](https://www.linkedin.com/in/annamiller/) in a [LinkedIn Live](/speaking/job-search-from-search-to-final-rounds-in-2-months). Same search, live reactions included. 🎙️
If you're in a job search right now, I'd love to hear what it's actually like for you. Not the version you post on LinkedIn. The real one. 💙
## why I rebuilt my whole site from scratch
URL: https://mindiweik.com/blog/why-i-rebuilt-my-site/
Published: 2026-07-03
I finally tore down the old site. Not because it was broken. Because I wanted to build it from scratch.
Why the heck would you rebuild a site that already works?
Fair.
## the old site was a rental, not a home
For a while my whole online presence lived at Substack and later [wip-podcast.com](https://www.wip-podcast.com/), built on a website builder that did exactly what website builders do. It got me online fast, it looked fine, and it quietly boxed me in the second I wanted anything specific. And I kept wanting things specific.
A few things nagged at me for months:
1. **It was named after one project.** wip-podcast.com is a great name for a podcast and a weird name for a person who also writes, speaks, and ships side projects. My work doesn't live in one box, so my site shouldn't either.
2. **I didn't own the layer that mattered.** I owned the content, sure. But the structure, the components, the way things connected? That belonged to the builder. Every customization was a fight against defaults.
3. **I think in release notes, versions, and commits.** I wanted my homepage to literally be a changelog of everything I ship: posts, episodes, talks, projects, all in one feed. Try doing that in a drag-and-drop builder. I dare you.
4. **Can I just say "components?"** I don't like building the same thing over and over!
There's a difference between renting a space and owning one. The rental is faster to move into. But you can't knock down a wall.
## what I actually wanted
Once I let myself imagine starting over, the wishlist got clear fast. One place, under my own name, that could hold all of it: the [WIP] Podcast, my blog, the talks, and the things I build. Not four scattered links. One home with four rooms.
And I wanted the site itself to feel like it was made by an engineer, because it is.
So [mindiweik.com](https://mindiweik.com/) became the plan. Freshly built alongside AI. Mine end to end.
## the design idea I'm most excited about: colorful wayfinding
Most personal sites pick one accent color and sprinkle it everywhere. Pretty, but the color isn't doing any work.
I wanted color to mean something. So each zone of the site gets its own identity:
- Blog is blue (it's also the home base)
- Podcast is pink (not for alliteration, pink was the theme beforehand)
- Speaking is green
- Projects are amber
Land on a pink page and you know you're in podcast-land before you've read a word. The color is navigation, not decoration. It's the kind of small systems-thinking detail that hardly anyone consciously notices and everybody feels.
## building it the boring, durable way
I rebuilt on [Astro](https://astro.build/) as a static site. In plain terms: it's fast, it ships almost no JavaScript, and there's no platform between me and my own pages. Publishing a post is a git push. A GitHub Action builds the site and deploys it while I go pour my tea.
Every stack decision came down to one question: will I still understand and control this in two years? Role-named design tokens instead of hardcoded colors. Components that each do exactly one job. A single file that maps each zone to its color and route, so adding a new section is one line, not a scavenger hunt.
It is, on purpose, a little boring. Boring is what survives.
## the part I didn't expect
I expected the rebuild to be a chore with a nice reward at the end. What I didn't expect was what the migration itself would do to me.
Moving three years of content means rereading three years of your own writing. Posts from right out of bootcamp. Posts from the first engineering job. Episode notes from conversations long past. It turned into an accidental retrospective, and I came out the other side weirdly proud of the person who wrote all of that while figuring everything out in public.
The other surprise: owning the site changed my relationship with ideas. On the builder, "it would be cool if..." went to a wishlist and died there. Now? I wanted drafts I could preview locally without publishing, and scheduled posts that go live at 6am while I am running outside with my doggo. I built both in an afternoon. The distance between wanting a thing and shipping a thing collapsed, and that momentum spills into everything else I make.
## was it worth it?
Rebuilding from scratch is almost never the efficient choice. If you just want to be online, don't do this. Use the builder, ship the thing, go live your life.
But if the site is going to represent you for the foreseeable future, and the tools keep telling you no, sometimes the fastest path forward is to start over on a foundation you actually own.
**So: what's the thing you keep meaning to rebuild from scratch? I'd genuinely love to hear about it. 💙**
## level up: sources, performance & your playground
URL: https://mindiweik.com/blog/level-up-sources-performance-and-your-playground/
Published: 2026-03-26
You've made it to the finale. 🎉
In Parts 1-3, we covered the Elements tab, the Console, the Network tab, and the Application tab. If you've been following along and actually opening DevTools while you read, you're on top of things.
Now we're going deeper.
This is Part 4 of a 4-part series:
1. Meet Your Browser's Toolbox
2. Your New Best Friend: The Console
3. What's Actually Happening: Network & Application
4. **Level Up: Sources, Performance & Your Playground (you are here! 👋)**
Let's finish this.
## the sources tab
The Sources tab is where debugging gets serious.
Open DevTools and click **Sources**. You'll see a file tree on the left where all the JavaScript, CSS, and HTML files your browser loaded. Click into any JavaScript file and you're looking at the actual code running in your browser.
Here's the thing: most new devs default to sprinkling `console.log` everywhere when something breaks. That works! But breakpoints are faster, more powerful, and make you look like a wizard to anyone watching.
### setting a breakpoint
1. Open the Sources tab
2. Navigate to a JavaScript file in the file tree
3. Click the line number where you want to pause execution
That's it. A blue arrow appears on the line. Now when that line of code runs, your browser will pause, and you can inspect everything: variables, the call stack, what's in scope.
### stepping through code
Once you're paused at a breakpoint, you have controls in the top right of the Sources panel:
- **Resume** (blue play button) - continue running until the next breakpoint
- **Step over** - run the current line and pause at the next one (don't go inside function calls)
- **Step into** - go inside the function call on the current line
- **Step out** - finish the current function and pause when you return
This is how senior engineers debug. They don't guess. They pause, look around, and step through the code line by line until they find exactly where things go wrong.
### conditional breakpoints
Right-click a line number and choose **Add conditional breakpoint**. You can type an expression like `count > 5` and the browser will only pause when that condition is true. Incredibly useful for loops where the bug only shows up on a specific iteration.
## the performance tab
The Performance tab answers a question you'll hear a lot as you grow: _why is this so slow?_ I don't see many devs using this tool and I don't use it as often as I probably could. 😅
Open DevTools and click **Performance**. You'll see a big empty space and a record button.
### recording a performance profile
1. Click the **record button** (or press `Cmd + E` on Mac, `Ctrl + E` on Windows)
2. Interact with your page - scroll, click, load something
3. Click **stop**
Now you'll see a timeline. It looks intimidating at first, but here's what to focus on:
**The flame chart** is the call stack over time. Tall stacks mean a lot of functions calling each other. Long horizontal bars mean a function ran for a long time. Long bars are your bottlenecks.
**The summary panel** at the bottom shows a breakdown of where time was spent: Scripting, Rendering, Painting, etc. If Scripting eats most of your time, your JavaScript is the culprit. If Rendering is high, your layout or CSS might be thrashing.
### what to look for
- **Long tasks** - anything over 50ms is flagged in red. These are the tasks that make your UI feel crummy.
- **Layout thrashing** - repeatedly reading and writing to the DOM forces the browser to recalculate layout over and over. It shows up as alternating purple (layout) and green (paint) blocks.
- **Unnecessary re-renders** - in frameworks like React, this is a common culprit. The Performance tab will show you exactly where time is being spent.
You don't need to master the Performance tab. But knowing it exists and how to open a recording means that when someone says "this page feels slow," you have a tool to actually find out why.
## the lighthouse tab
Lighthouse is an automated auditing tool built right into DevTools. It's one of the most useful things to run on any project you care about.
Click **Lighthouse** in the DevTools tabs. You'll see options to audit for:
- **Performance** - how fast does your page load?
- **Accessibility** - can everyone use your site?
- **Best practices** - are you following web security and quality standards?
- **SEO** - is your page set up to be found by search engines?
Check the categories you want and click **Analyze page load**.
After a minute or so, you'll get a scored report (0-100 for each category) with specific, actionable recommendations. Things like:
- "Images are not sized correctly" (with exact file names)
- "Links do not have a discernible name" (accessibility)
- "Page lacks a meta description" (SEO)
- "Render-blocking resources" (performance)
Each finding links to documentation explaining why it matters and how to fix it. It's like a free code review from a robot.
Run Lighthouse on your side projects. You'll learn a lot about what "production-ready" actually means.
## the full catz4life sandbox walkthrough
If you've been following this series, you've had access to the [Catz4Life Adopshun Centre](https://github.com/mindiweik/catz4life) sandbox since Part 1. Now let's pull it all together.
Here's a quick guide to get started if you haven't yet:
1. Fork or clone the repo: `git clone https://github.com/mindiweik/catz4life`
2. Open `index.html` directly in your browser (no server needed)
3. Open DevTools and start exploring
Hints mode is available if you want a nudge: set `HINTS = true` in `script.js`.
### what's broken and where to find it
**Elements tab**
There's a typo in the page heading. Inspect the DOM and find it.
One of the cat cards has a CSS issue making it look ugly. Use the Styles panel to find and fix it.
**Console tab**
The "Adopt Me!" button on one card throws an error when clicked. Open the Console, click the button, and read the error message. The fix is one line.
**Network tab**
One of the cat images fails to load. Watch the Network tab as the page loads and find the failed request. Check the URL for a typo.
**Application tab**
There's a theme toggle in the UI. Find where it stores its state in localStorage. Try changing the value manually and refreshing.
**Sources tab**
Set a breakpoint on the button click handler and step through the code. Where does it go wrong?
**Lighthouse**
Run a Lighthouse audit on the page. What's the accessibility score? There are at least two issues to find.
## your browser is a workbench
Here's the thing about DevTools: it's not a tab you open when something breaks. It's a workbench you keep open while you build.
The engineers who get fast aren't the ones who know the most syntax. They're the ones who can look at a broken thing and immediately know which tool to reach for. Network tab for API issues. Console for JavaScript errors. Sources for stepping through logic. Lighthouse for quality checks.
That instinct takes practice. But the fact that you've read all four parts of this series means you know the tools exist and roughly what they do. That's the hardest part!
Now go break something on purpose.
## your final catz4life challenge 🐱
Fork the repo. Open DevTools. Fix every bug.
Then, once you've fixed them, break something intentionally and see if you can use DevTools to catch what you broke. That's the real exercise: learning to trust your tools.
Happy debugging. 💖
---
_Thanks for reading the whole series! Questions, lightbulb moments, or things I missed? Drop a comment or find me on LinkedIn._
_← [Part 3: What's Actually Happening: Network & Application](/blog/whats-actually-happening)_
## what's actually happening
URL: https://mindiweik.com/blog/whats-actually-happening/
Published: 2026-03-12
If you've been following along, you've poked around the DOM, styled things on the fly, and debugged with the Console like a pro. Nice work.
Now we get to the tab that makes senior engineers feel like they have superpowers.
The Network tab displays what your browser is doing behind the scenes. Every request. Every response. Every failure. It's like x-ray vision for your web app.
This is Part 3 of a 4-part series:
1. Meet Your Browser's Toolbox
2. Your New Best Friend: The Console
3. **What's Actually Happening: Network & Application (you are here! 👋)**
4. Level Up: Sources, Performance & Your Playground
Let's get into it.
## the network tab
Open DevTools and click the **Network** tab. If it looks empty, refresh the page.
Suddenly: a waterfall of requests. Every image, every script, every API call, every font should all appear, in the order it happened, with timing, status codes, and response data attached.
Here's the thing: most debugging conversations I've had that started with "my API call isn't working" ended the second someone opened the Network tab. The answer is almost always right there.
## http status codes (the ones you actually need)
Before we start breaking things on purpose, a quick cheat sheet:
- **200** - success. All good.
- **201** - created. Your POST worked.
- **204** - no content. Success, but nothing to return.
- **301 / 302** - redirect. The URL moved.
- **400** - bad request. You sent something wrong.
- **401** - unauthorized. Not logged in (or bad token).
- **403** - forbidden. Logged in, but no access.
- **404** - not found. That route/resource doesn't exist.
- **405** - method not allowed. Wrong HTTP verb.
- **422** - unprocessable entity. The data you sent was valid JSON but failed validation.
- **500** - server error. Something blew up on the backend.
- **503** - service unavailable. The server is down or overloaded.
You'll see these in the Network tab. Learning to read them is half the debugging battle.
## a working request, then some chaos
Open a new browser tab, open DevTools to the Network tab, and paste this into the Console:
```js
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((res) => res.json())
.then((data) => console.log(data));
```
Switch back to the Network tab. You'll see a new request appear - `1` - with a status of `200`. Click it. You can inspect the request headers, the response headers, and the actual response data.
That's a healthy request. Now let's break some stuff.
## 4 ways things go wrong (and what they look like)
### 1. bad url (failed request)
```js
fetch('https://this-url-does-not-exist-at-all.xyz/api').catch((err) => console.error(err));
```
No status code. No response. Just a red `failed` entry in the Network tab. This happens when the DNS lookup fails because the server doesn't exist. Classic typo in a base URL.
### 2. wrong endpoint, 404
```js
fetch('https://api.thecatapi.com/v1/imaaaages/search').then((res) => console.log(res.status));
```
Spot the typo? `imaaaages` instead of `images`. The server exists, but the route doesn't. You'll see a clean `404` in the Network tab. This is one of the most common bugs I've seen new devs spend 20 minutes on and it's a one-character fix.
### 3. cors issues
```js
fetch('https://www.google.com').catch((err) => console.error(err));
```
This one will fail with a CORS error. You'll see it in both the Console and the Network tab. CORS (**Cross-Origin Resource Sharing**) is the browser's way of preventing one website from making requests to another without permission. The Network tab will show the request as blocked, and the Console will tell you exactly which header is missing.
CORS errors are confusing when you first encounter them, but they always follow the same pattern. Check the Network tab, look for the blocked request, and look for the `Access-Control-Allow-Origin` header in the response.
### 4. wrong http method, 405
```js
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'DELETE',
}).then((res) => console.log(res.status));
```
The endpoint exists, but it doesn't accept DELETE requests (or whatever method you used). The server responds with a `405 Method Not Allowed`. If you're getting a 405, double-check your HTTP verb; it's almost always a `POST` where there should be a `PUT`, or a `GET` where there should be a `PATCH`.
## the application tab
Okay, Network tab handled. Now let's talk about the **Application tab** which is less glamorous but super duper useful.
This is where your browser stores data: cookies, localStorage, sessionStorage, IndexedDB, and more. Click the Application tab in DevTools.
On the left sidebar you'll see a tree of storage options. The ones you'll use most:
- **Cookies** - small pieces of data your browser saves per domain. Often used for session tokens and user preferences.
- **Local storage** - key/value pairs that persist even after you close the browser. Commonly used for things like dark mode preferences, cached data, or "remember me" flags.
- **Session storage** - same as local storage, but cleared when you close the tab. Good for temporary state.
## viewing and editing stored data
Click **Local storage** in the sidebar, then click your domain. You'll see a table of keys and values. You can:
- **Click a value** to edit it in place
- **Right-click** to delete an entry
- **Add new entries** with the "+" button
This is incredibly useful for debugging auth or other workflows. If you're logged in and something is acting weird, check your cookies for your session token. Is it there? Is it expired? Is it malformed? The Application tab will show you.
Real-world example: you're building a theme toggle. Light mode, dark mode, whatever. You store the preference in localStorage as `theme: "dark"`. If the toggle isn't working, open the Application tab and check: is the value actually being set? Is it being read correctly? You can manually change the value and refresh to test both states without touching your code.
## your catz4life challenge 🐱
Back to the [Catz4Life Adopshun Centre](https://github.com/mindiweik/catz4life). Open the project and the Network tab at the same time. Somewhere in the app there's a broken API call. Watch the Network tab as you interact with the page and find the failed request to figure out what went wrong.
Then check the Application tab. There's a theme toggle buried in the UI. Find where it stores its state, change the value manually, and see what happens on refresh.
Hints mode is still there if you need it: `HINTS = true` in `script.js`.
## what's next
In Part 4 (the finale!), we're covering Sources, Performance, and Lighthouse plus a full guided walkthrough of the Catz4Life sandbox with all the bugs called out by tab.
But before you get there: next time an API call isn't working, open the Network tab before you change a single line of code. The answer is almost always already there.
Happy debugging. 💖
_Questions, lightbulb moments, war stories about CORS? Let me know!_
← [Part 2: Your New Best Friend: The Console](/blog/your-new-best-friend-the-console) | [Part 4: Level Up](/blog/level-up-sources-performance-and-your-playground) →
## your new best friend, the console
URL: https://mindiweik.com/blog/your-new-best-friend-the-console/
Published: 2026-02-25
If you made it through [Part 1](/blog/unlocking-your-browser), you've already "hacked" Wikipedia, poked around the DOM, and maybe changed a button color or two. Good. You're getting comfortable being curious.
Now we're going deeper.
The Console tab is where things start to feel a little like magic. And also where debugging goes from "why is nothing working and I want to cry" to "...oh. OH. I see it now."
This is Part 2 of a 4-part series:
1. Meet Your Browser's Toolbox
2. **Your New Best Friend: The Console (you are here! 👋)**
3. What's Actually Happening: Network & Application
4. Level Up: Sources, Performance & Your Playground
Let's get into it.
## what even is the console?
The Console is a live JavaScript environment running directly on whatever page you have open. You can write code, run it instantly, and see what happens without touching your actual codebase.
Think of it like a scratchpad for your browser. A place to test ideas, poke at things, and get real-time feedback on what's happening inside your page.
Here's the thing: most developers open the Console when something breaks. But some of the best developers open it before things break.
## opening the console
You might already have it open from Part 1. If not:
**Chrome / Edge / Brave:**
- Mac: `Cmd + Option + i` or `Cmd + Option + j`
- Windows/Linux: `F12` or `Ctrl + Shift + i`
**Firefox:**
- Mac: `Cmd + Option + i`
- Windows/Linux: `F12` or `Ctrl + Shift + i`
**Safari:**
- First, you need to enable the Developer menu: Go to **Preferences > Advanced** and check "Show Develop menu in menu bar"
- Then: `Cmd + Option + i`
You'll see a blank panel with a `>` prompt. That's your playground.
## 6 things you can do right now
These are the demos I ran during my [Parsity.io](https://parsity.io) Tech Talk. Try each one on any page you have open.
### 1. target and change content
```js
document.querySelector('h1').textContent = 'I own this now 😈';
```
This grabs the first `h1` on the page and replaces its text. Change `'h1'` to any CSS selector: a class, an ID, a button. This is how you test content changes before touching your code.
### 2. style on the fly
```js
document.body.style.backgroundColor = 'hotpink';
```
Instant hotpink. You're welcome. Swap in any CSS property (camelCase) and any value. This is faster than toggling between your editor and browser when you're trying to nail a style.
### 3. log for debugging
```js
console.log('hello from the console 👋');
```
Okay, this one looks simple, and it is. But `console.log` is the most underused debugging tool I see new devs skip over. When something isn't working, log everything. Log your variables, log your API responses, log your function outputs. The Console will tell you exactly what your code is seeing, which is usually different from what you think it's seeing.
Also worth knowing: `console.warn()` and `console.error()` give you yellow and red styling respectively which is handy when you want certain logs to stand out.
### 4. explore page context
```js
window.location;
```
This returns an object with everything about the current URL like the full `href`, the `hostname`, the `pathname`, query params, and more. It's incredibly useful when debugging routing issues or building something that depends on URL structure.
Try `window.location.pathname` to get just the current path. Or `window.location.search` to see query string parameters.
### 5. make the entire page editable
```js
document.body.contentEditable = true;
```
Yes, really.
Run this and then click anywhere on the page. You can type, delete, edit. The whole page becomes a document! Refresh to undo. This is fun for testing copy changes or doing a quick "how does this look with shorter text?" gut check.
### 6. list all images on a page
This grabs every `img` element on the page and logs its source URL. Useful for auditing what images are loading, debugging missing images, or just being nosy about where a site's assets live.
```js
document.querySelectorAll('img').forEach((img) => console.log(img.src));
```
## the bigger picture
These six demos barely scratch the surface of what the Console can do. But they illustrate the core idea: **the Console lets you interact with a live page as if you wrote the code yourself.**
It's the fastest way to:
- Test a function before adding it to your codebase
- Figure out why a variable isn't what you expect
- Check what data an API is actually returning
- Prototype a small interaction without spinning up a dev environment
Next time you're debugging, try this: before you change anything in your code, open the Console and log the thing that's confusing you. Nine times out of ten, that log will tell you exactly what's wrong.
## your catz4life challenge 🐱
Remember the [Catz4Life Adopshun Centre](https://github.com/mindiweik/catz4life) from [Part 1](/blog/unlocking-your-browser)? There's a broken "Adopt Me!" button that's supposed to do something when you click it...but it doesn't.
Open the project in your browser, open the Console, and see if you can figure out what's happening.
(Hint: check for any errors that show up automatically when the page loads. The Console is already watching.)
If you want some guidance, set `HINTS = true` in `script.js` to turn on hint mode.
## what's next
In Part 3, we're going to the Network tab where you can watch every single request your browser makes in real time. API calls, failed requests, CORS errors, HTTP status codes. It's where the really juicy debugging happens.
**Until then:** open the Console on every site you visit. Run a `console.log`. Change a color. Make something editable. **Get weird with it.**
The Console doesn't bite. 💖
_Did something click for you? I'd love to hear about it!_
← [Part 1: Meet Your Browser's Toolbox](/blog/unlocking-your-browser) | [Part 3: What's Actually Happening](/blog/whats-actually-happening) →
## unlocking your browser
URL: https://mindiweik.com/blog/unlocking-your-browser/
Published: 2026-02-18
## your browser isn't just for scrolling.
The browser is actually your secret weapon for building, debugging, and understanding the web.
Last August I presented a Tech Talk for the folks in bootcamp phase at [Parsity.io](https://parsity.io). It was a blast! I was excited to share my rediscovery of the neat things your browser can do. See, I'd just moved to a frontend-focused team after spending the beginning of my developer career building backend APIs where the browser was never the intended client. I'd kind of... forgotten how powerful it is.
Before I switched careers into tech, I thought my browser was just for looking at websites. Reading articles, checking email, watching videos. The usual stuff.
Turns out it's also one of the most powerful tools in your developer toolkit.
If you're new to web development (or even if you've been at it for a while but haven't really explored DevTools lately), this short series is for you. We're going to crack open that toolbox and see what's inside.
This is **Part 1 of a 4-part series**:
1. **Meet Your Browser's Toolbox** (you are here! 👋)
2. Your New Best Friend: The Console
3. What's Actually Happening: Network & Application
4. Level Up: Sources, Performance & Your Playground
Let's get into it.
## what browser do you use?
Chrome? Firefox? Safari? Edge? Brave? Something else entirely?
It doesn't really matter for what we'll cover. All modern browsers have similar developer tools built in. But it's worth knowing the landscape because they're not _exactly_ the same.
### brief version of the browser landscape
The major players are:
- **Chrome** (and Chromium-based browsers like Edge and Brave)
- **Firefox**
- **Safari**
What makes them different under the hood? Their **rendering engines**. These interpret your HTML, CSS, and JavaScript and turn it into what you see on screen.
- **Blink** powers Chrome, Edge, Brave, and Opera. It dominates the market (around 70%+ of users), which is why Chrome-like DevTools are the most common.
- **Gecko** powers Firefox. Firefox is known for privacy-first features and has some unique DevTools, like an advanced CSS Grid inspector.
- **WebKit** powers Safari. It's stricter with web standards and security, and it's the default on all iOS devices, making it critical for mobile testing.
### why does this matter for devtools?
As a developer, keyboard shortcuts and layouts differ slightly between browsers. Some CSS and JavaScript features behave a little differently. And if someone ever asks you "Why does my site look different in Firefox vs Chrome?" you could probably pin it down to rendering engine quirks.
But here's the thing: the core functionality of DevTools is remarkably similar across all of them. Once you learn one, you can navigate the others.
## opening devtools: your first step
Before we dive in, let's make sure you know how to actually open these tools.
**Chrome / Edge / Brave:**
- Mac: `Cmd + Option + i` or `Cmd + Option + j`
- Windows/Linux: `F12` or `Ctrl + Shift + i`
**Firefox:**
- Mac: `Cmd + Option + i`
- Windows/Linux: `F12` or `Ctrl + Shift + i`
**Safari:**
- First, you need to enable the Developer menu: Go to **Preferences > Advanced** and check "Show Develop menu in menu bar"
- Then: `Cmd + Option + i`
Once you've got them open, you'll usually see a panel at the bottom or side of your browser with a bunch of tabs. Think of DevTools as a **toolbox with different drawers**. Each drawer (tab) has specific tools for specific jobs.
Today we're opening the first drawer.
## elements: inspecting & editing the dom
The **Elements** tab shows you the HTML structure of the page you're looking at. This is the Document Object Model (DOM), which you can think of as the skeleton of the webpage.
What you can do here:
🔍 Click on any element to see its HTML and CSS
✏️ Edit text directly on the page (it won't save, but it's great for testing!)
🎨 Tweak CSS properties to see how they affect the design
👻 Hide or show elements
### try this right now
Seriously. Do it. I'll wait.
1. Go to any website ([Wikipedia](https://en.wikipedia.org/wiki/Paragliding) works great for this)
2. Open DevTools
3. Right-click on a headline and select **"Inspect"**
4. Double-click the text in the Elements panel
5. Change it to something silly like "[YOUR NAME] WAS HERE!"
You just "hacked" Wikipedia! 🎉
(Not really, though. Your changes only exist in your browser and will reset when you refresh. No websites were harmed in the making of this tutorial.)
### why this is actually useful
This isn't just a party trick. The Elements tab is incredibly practical when you're building something:
- **Testing CSS changes quickly** without touching your actual code. Want to see if that button looks better with more padding? Just change it in the Elements panel first.
- **Debugging layout issues.** Why is that div not showing up where you expected? Inspect it. Check its dimensions, margins, padding. The box model visualization is right there.
- **Learning from other sites.** See a cool design on someone's website? Inspect it! Look at how they built it. This is one of the best ways to learn CSS.
- **Quick content previews.** Writing copy for a client? Edit the text right on the page to see how it looks before committing to anything.
### pro tips for the elements tab
1. **Hover to highlight.** As you move your mouse over elements in the panel, they'll highlight on the page. This is the fastest way to figure out "what element is _that_?"
2. **The pick tool.** See that little cursor icon in the top-left of DevTools? Click it (or press `Cmd + Shift + C` on Mac), then click anything on the page to jump straight to it in the Elements panel.
3. **Force element states.** Right-click an element in the panel and look for "Force state." You can force `:hover`, `:active`, `:focus`, and `:visited` states. Super handy for debugging interactive styles without actually hovering.
4. **Computed tab.** Next to the Styles pane, there's a "Computed" tab that shows you the _final_ CSS values after all the cascading and specificity battles have been fought. When you can't figure out why your style isn't applying, this is where the truth lives.
## want to practice? meet catz4life 🐱
Throughout this series, I'll be pointing you to a silly project I built called the **[Catz4Life Adopshun Centre](https://github.com/mindiweik/catz4life)**. It's a single-page cat adoption site (pulling from The Cat API, because obviously) with intentional bugs for you to find and fix.
For this post, here's your Elements tab challenge:
- There are **typos in the headlines** that need fixing
- The **CSS colors are... a choice** 😬 (see if you can make them less offensive)
- Try hiding and showing elements to see how the layout changes
Grab it here: [github.com/mindiweik/catz4life](https://github.com/mindiweik/catz4life)
Download or fork it, open `index.html` in your browser, open DevTools, and start poking around!
## what's next
In **Part 2**, we're getting into the **Console**, your new best friend for debugging. We'll run JavaScript on live pages, change styles with code, make entire pages editable, and more. It's going to be fun.
Until then, here's your homework: **Open DevTools on every website you visit this week.** Inspect things. Change colors. Edit headlines. Get curious.
The best developers I know aren't the ones who memorized every CSS property. They're the ones who aren't afraid to poke around and see what happens.
Go forth and break something. 💖
[Part 2: Your New Best Friend: The Console](/blog/your-new-best-friend-the-console) →
## building a compass
URL: https://mindiweik.com/blog/building-a-compass/
Published: 2026-01-27
On two recent occasions, I was asked where I see myself in the future. My answer in both cases was relatively similar...
**I don't know. 🤔**
I'm not lost. I have a general sense of where I'd like north to point. At the same time, tech is moving at hectic speeds through multiple stages of evolution.
My job looks different from what I expected when I became a Software Engineer a few short years ago. Now the map feels more like a whiteboard someone is actively erasing as I'm writing on it. And I know it's not just Software Engineering, it's a similar feeling across most of the tech sphere and beyond.
And honestly?
**I think more of us should feel allowed to say that.**
## **I’m not unmotivated**
It's tough to say where we will end up, I _am_ still energized by all the things we can build and do with the plethora of tools that exist and will someday come to be. But, it's also exhausting to keep up with. The work keeps changing and things are unclear.
It's not just because AI is "here now." Though, of course, AI has a lot to do with this pace, bringing significant change in its wake. (Compare to my [first post on using AI](/blog/how-to-start-working-with-ai) from barely 2 years ago! 🤯)
The entire _shape_ of software engineering has changed. Skills that were "optional" when I started are now baseline. Take a look at some of the job descriptions out there for newbies. The demands are insane sometimes. I don't even qualify for some wish lists. 😵💫
Because of this rapid pace we're expected to know more, output more, and move faster all the while. And the parts we thought would stay human (at least for a bit) are being automated, scaled, rewritten, and redefined in real time, including some of the more creative endeavors.
So when someone asks where I’ll be in [_n_] years, I can’t give them a neat answer.
**What I _can_ say is what I’m building instead:**
✅ adaptability
✅ balance
✅ a career that can take hits and keep evolving
## **career ladders are actually jungle gyms**
Even in v0 of the [WIP] Podcast, it's clear to me that no journey is the same, nor is any journey I've heard straightforward.
Humans are consistently experimenting, iterating, and trying to see where we belong whether we do it intentionally or not.
More than ever, tech doesn’t feel like a ladder. Layoffs are rampant and some folks have had to start anew as opportunities dry up or change drastically from what they once were. So many people I've talked to in tech now are also career changers who came from various industries and trades rather than a traditional computer science or technical degree. [Myself included](/blog/the-power-of-career-change)!
You climb sideways.
You hang for a while.
You pivot.
You rebuild.
You take a break because your nervous system is throwing error codes. And somehow, that’s still progress.
Adaptability isn’t being okay with change.
Adaptability is staying functional while the rules keep updating.
## **“I don’t know” moments**
### **😱 moment #1: inquiry about “the future”**
I was talking with my therapist about this afterward, too. (Yay therapy!) I shared those recent moments where I had been asked where I think I'll be in [_n_] [_timeframe_] and shared my concerns about how much I feel I really can't answer that clearly.
We talked about how my brain did that thing where it tried to generate the “correct” response. Senior engineer? Leadership? Architecture? Product? Something impressive?
In retrospect, the honest answer is simpler: Focus on what I can actually control. Right now.
**“I don’t know. I’m focused on building skills that survive change.”**
Because the truth is: **we've seen too many ‘stable paths’ disappear mid-walk.** Especially in the last few years.
### **🤨 moment #2: the definition changed**
Earlier this year I started a new job with new challenges. It was exciting, as it is for most of us when starting something new! But, there was also a lot of new things and unknowns to I didn't expect to grapple with related to all of this consistent change.
It used to be enough to:
- know your stack
- ship features
- write clean code
Now it’s also:
- navigating AI tools + automation
- building systems people can trust
- understanding system health + observability
- collaborating across functions constantly
- communicating clearly through ambiguity
The job didn’t just evolve. **The scorecard changed.**
So “I don’t know” isn’t actually a weakness. It’s just… accurate. **And it's 100% okay to acknowledge and say it.**
## **adaptability requires balance**
Have you noticed a trend on LinkedIn? Online in general? It's called burnout. Everyone is talking about it. I've been there. Done that. (See [previous post](/blog/3-habits-that-helped-me-recover-from-burnout-and-stay-motivated).)
What I've learned in the last year in particular is that adaptability also needs a steady helping of balance. Although we're moving quickly, hustle culture is not going to help us keep pace.
👉 “Stay sharp.”
👉 “Always be learning.”
👉 “Keep up or fall behind.”
Okay, but… _**when do you get to be a person?**_
I've been feeling lately that this seems to be another one of those "go slow to go fast" times I hear so much about in tech. I've tried to embrace it more and more lately.
Over the last several months I took a nice long break outside of my day job. What did I find? I had much clearer thoughts and I things started to "click" better that eluded me when I was walking directly toward burnout. And I also felt a new sense of energy and excitement about the new problems I was tackling.
Balance isn’t a reward for success. **It's what lets you stay in the game long enough to evolve.**
## **3 sustainable ways to grow your adaptability**
### **1 - ⚓️ anchor to skills, not titles**
Titles change. Teams change. Tech stacks change. But transferable skills continually show up like:
- systems thinking
- debugging
- communication
- learning fast
- writing clearly
- collaborating smoothly
- shipping + iterating
Ask yourself: “If my stack disappeared tomorrow, what would still make me valuable?” That answer is your foundation.
### **2 - 🧪 run small experiments often**
You don’t need a full reinvention every time the industry shifts. Instead, work with something new and small:
- add one new tool into a project
- pair with someone from another specialty
- a mini internal demo / lightning talk
- join a cross-functional project
- a weekend “curiosity build”
Small experiments teach you:
- I can be new at something and survive it
- I can learn without turning it into pressure or judgement
- I can evolve without self-erasing
Adaptability is built through practice, not panic.
### **3 - 🛠️ build a balance system you can actually maintain**
Balance isn’t goofing off while pretending deadlines don't exist. It’s having a pace you can trust.
Some examples:
- a **weekly learning window** (60-90 minutes, not a second job)
- “no learning after 8pm”
- “one new thing at a time”
- a low-pressure curiosity list
- intentional rest without guilt
Your career isn’t a hackathon and you don’t need to optimize your entire life. I still have to remind myself these things often!
## **a better question**
If someone asks me today where I see myself, I am certain that I will still say, **“I don’t know.”**
But now I would also add: **“I’m building a career that can survive change.”**
I need a compass, not a map on this journey. Because the goal isn’t predicting the future when the landscape is evolving. The goal is becoming someone who can handle whatever shows up next, who can navigate any terrain.
Maybe “I don’t know” isn’t uncertainty. Maybe it’s clarity. The future’s moving fast, so I’m building adaptability and protecting balance. That’s the plan.
**If you feel behind, you’re not broken. The pace is real. Start small. Stay steady. Keep becoming. 💖**
## ✨ introducing... [wip]
URL: https://mindiweik.com/blog/introducing-wip/
Published: 2025-06-11
Over the last year and a half I’ve experimented with **codeOutLoud** as a conduit. It’s been fun! Knowledge has been gained and significant growth attained both personally and professionally since the inception of this Substack.
_During the last few months, I’ve done a lot of thinking._ 🤔
You may - or may not - have noticed a recent, subtle rebrand as I tried to formalize my thoughts, goals, “personal brand,” and what’s in store for the future. Alas, it’s not quite enough. At least for me.
## here’s what’s been on my mind:
- I want to get more creative!
- I want to diversify my own approach to learning new things!
- I want to collaborate with more people!
- I want to share technical stuff _and_ human stuff!
So → I’m rebranding once more with more intention!
Plus, a new podcast will come to life that has been brewing internally for a while! 🎙️
## [WIP]: human development in tech
_**[WIP]: human development in tech**_ is both a substack newsletter _and_ a podcast about the messy, honest, and ongoing process of growth - featuring real stories from people in tech who are all a _work in progress_.
If you’re not familiar, “WIP” is often used to declare that a pull/merge request, technical document, or a project is in “draft” mode. It’s a **Work In Progress**!
What does this mean moving forward?
## newsletter
**Cadence: (mostly) bi-weekly to monthly**
Newsletter posts about things I’m trying out, learning about, and experiencing will continue. Business as usual in that department, but with a new name and face!
## podcast
**Launch: Sometime in July 2025**
**Cadence: (ideally) weekly**
The intent is to humanize the tech industry with curiosity as we learn about the ups and downs, the lessons, and the progress discovered in tech.
A majority of the episodes will feature guests! I will reach all across the industry including not only engineers, but also support, product, design, marketing, strategists, coaches, and more! Some smaller snippets will be sprinkled in to share my own learning moments and - perhaps - learning moments from others.
## here’s a glimpse at the new branding 👀
## join me!
Over the next several weeks, you may see changes to reflect the new name and face that will trickle into all existing posts and upcoming new posts alongside my new YouTube channel and personal branding on LinkedIn.
**If you’re interested in following along, Substack will be my “home base,” meaning:**
- You can subscribe for free to receive emails/app alerts for new posts and episodes.
- If you prefer to watch/listen elsewhere, all episodes will trickle out into other podcast avenues like YouTube, Spotify, Apple Podcasts and more!
- Shortly after launch, I would like to open up the chat feature in Substack. It’s the best way to get quick feedback and have discussions about your own [WIP]!
See you soon with a new name and face! 😅 ✨ 💖
## the importance of open telemetry
URL: https://mindiweik.com/blog/the-importance-of-open-telemetry/
Published: 2025-05-27
If you don’t like new, shiny things…then I’m not sure you’re human.
Just kidding! Often we are enamored with shiny object syndrome in tech and as someone who has had the luxury of working on mostly greenfield projects, it’s usually for good reason.
It’s fresh, it’s clean, we can do what we want! To some extent, yes, but it’s hard to know how much you need something until you don’t have it because you didn’t set it up yet.
In my case, I didn’t know just how much we needed [Open Telemetry](https://opentelemetry.io/)! Let’s take a high-level look at what this is and how it can help in most projects.
_\*There are **oversimplifications** here to share foundational concepts at a high level._
**Here, we’ll cover:**
1. 👾 What is Telemetry?
1. 🤓 Real Example
2. 📊 What is Observability?
3. 🔭 What is Open Telemetry?
_Blasting off!_ 🚀
First, I want to share a bit of a story with a fun analogy I enjoyed using in my recent **Denver Gusto Lightning Talks from Women and Folks in Tech** lightning talk!
Here I have also gone into more detail than I was able to cover in my 5-minute lightning talk. _Talk recording at the end of this post for those interested!_
## have you ever tried debugging without logs?
When I started working on one of my first greenfield projects, we were still setting up our CI/CD pipelines for deployments and this was often my task.
We were logging within the application, to be fair. But, when our Kubernetes pods would restart those logs were lost! …More than once! It was frustrating to try to track down issues that popped up in the meantime. We looked into a few solutions, but didn’t find a good one we wanted to fully implement right away.
> "Debugging without logs is like being a detective at a crime scene where someone cleaned up all the evidence. 🕵️♂️ I was still trying to piece together what happened, but without fingerprints, witnesses, or a murder weapon.
**What could I do in the meantime?**
- Interview unreliable sources (like logs from our platform that calls our API service).
- Reconstruct the scene from scraps (like revisiting our code behavior).
- Rely on gut instincts and wild guesses.
It’s not impossible to solve bugs this way. However, it's frustrating, time-consuming, and prone to false leads.
_That’s where observability and telemetry come in! Let’s start small and build our way up, shall we?_
## 👾 what is telemetry?
I like to think of telemetry most simply as a _collection of data_.
But it’s more than just collecting data, especially in the context of OpenTelemetry. Telemetry refers to automatic gathering and sending of data from different parts of your system so you can see how everything is working and catch problems early.
This data is also what will be fed into our Observability tooling!
**There are 3 main aspects that make up telemetry data:**
- Logs
- Metrics
- Traces
### logs
Logs are text records of events that happen within your application. It’s an append-only data structure, usually including a timestamp and a message about the event.
In my case, we often use key-value pairs in JSON format which is called, “structured logging.” However, there are many opinions out there, as with most software-related practices, so there is no one perfect way to log events.
For instance, one API service I work with connects to a third-party vendor. Anytime we interact with a vendor resource (create, update, delete) we log that information.
_What resource was it, which customer does it belong to, what action did we take?_
Even some of the most mundane information can be surprisingly helpful when tracking down issues!
When something goes wrong, which can - and does - happen, it’s important to log the errors, too.
In the case of telemetry, logs are sent to a database with efficient storage that allows for filtering and searching.
### metrics
Metrics are numbers that help us track performance. It’s hard to measure something without numbers!
To best use metrics, it’s very helpful to have an idea of what measurements you want to track or a baseline of performance. This way you can tell whether or not your application is performing well.
There are 4 common types: counters, gauges, histograms, and summaries. I like to think of this as data that we can use to visualize what’s going on inside the application.
### traces
Consider these maps of what happened. You can follow the route to see what turns were taken and when.
This has, by far, been one of my favorite parts of exploring and working with the data received from [Open Telemetry](https://opentelemetry.io/)!
Not only can we see every _single_ step of the API request, but we can also see how long each step took and whether we connected to or called internal or external services. There’s so much we can see even beyond that, too!
Because we can now see previously invisible bottlenecks, we can identify what to change to improve performance. Another way to think of this is like security cameras for our app. We can rewind what happened and catch invisible issues in the act!
_**Let’s look at a recent example I encountered!**_
## 🤓 real example
While reviewing some of the data, we noticed in our **metrics** that there were several of requests that were taking give or take an average of ~8 seconds to respond.
That’s _WAY_ too long for what should be a fairly simple request that also didn’t take nearly that long when we developed it! 🤯
I was tasked with figuring out what was going on. I reviewed our **logs** and **traces** for offending events to track down the issue. Essentially, we had multiple similar calls to a vendor that didn’t appear to be necessary and slowed these responses _WAY_ down.

Blurry for added privacy and to capture all trace events. Highlighted portion = all the same call repeated multiple times
### _**what was happening?**_
Upon first glance, it appeared related to a code call that shouldn’t have been happening with the inputs provided. I was questioning, “how in the world is this getting called with an expected undefined input?” 🤔
I spent more time working with it and drilled deeply into our [Azure Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/visualize/insights-overview). Long story short, we were unwittingly making repetitive function calls when initiating an interface that _should_ have only happened once. 😱
We didn’t notice it because our local data set is MUCH smaller than our production data. As one might expect. So, we were making this call, but it was no big deal, it was fast. At production scale, however, it was taking far too long.
**Yikes!**
After a relatively simple refactor of how this interface was initiated and this particular value captured (and now stored) at the very beginning, I tested with Application Insights again locally and I was thrilled to find out that I fixed it! 😮💨
## 📊 what is observability?
As simply as possible, I would define observability as a collection of data. Specifically all of the telemetry data we already covered!
Of course, it’s more than just that.
In more complex terms, it’s a way that we can get a snapshot of the state of our system to understand the workload, the structure of our system including external services or load balancers or the like, and our resources in use.
Myriad tools exist for observability, some with more emphasis on certain telemetry data than others. Though not an exhaustive list, some examples include:
- Jaeger
- Prometheus
- Garfana
- New Relic
- Datadog
- Dynatrace
- Splunk
- Sentry
- **Cloud-native infrastructure monitoring:**
- AWS CloudWatch – Native observability for AWS infrastructure.
- Azure Monitor / Application Insights – Observability for Azure services and apps.
- Google Cloud Operations Suite (formerly Stackdriver) – GCP's native observability tools.
- …and there are more that exist!
The point is, there are a lot of options. To decide, you will also need to know what information is most important to your company, for your software, or whatever other stakeholder so you can capture and display that data most appropriately.
## 🔭 what is open telemetry?
Again, in the simplest terms: OTel is a free, open source framework. It aids in the collection and export of telemetry data to various observability tools.
It’s especially handy because it works with a wide variety of coding languages to ultimately _**standardize**_ your observability data from your applications. The biggest benefit is that you’re not locked into one tool or vendor! I believe that this is causing it to quickly become the industry standard for telemetry and observability.
Before [Open Telemetry](https://opentelemetry.io/), observability tools didn’t have standards. Every tool had a unique implementation, which made it hard to shift when it was needed or desired. This also meant that there was a fairly high barrier to entry - to use it in your application - because observability was complicated to implement if you had to know different processes for each service.
What if we wanted to add multiple services? _**Lookout, messy and complex code additions!**_
After we implemented Open Telemetry into our API services I was working with, we could now see what was previously invisible! 🫥
Open Telemetry does a great job of tying together all of the telemetry data so it can be exported to an observability tool. This is where we can make sense of it!
In my lightning talk I shared an oversimplified flow.
Open Telemetry flow
01 · instrumentation
Code or libraries to collect data
→
02 · sdk
Gather and organize the data
→
03 · exporter
Send the data to your observability tool(s)
Oversimplified Open Telemetry flow as shown in the presentation
Essentially:
- **Instrumentation -** We begin by adding the code or libraries into our application.
- In my case, this is initiated before the server starts up so that we can capture information about the pods or failed startup, if needed.
- **Software Development Kit -** The code/libraries then help to collect and organize the data, preparing it for send-off.
- **Exporter -** At the end, we send that data to our observability tool via Open Telemetry!
- It feels a little like magic when you see your data appearing in your tool. Very satisfying!
There are opportunities for customization, but better yet, there are “auto-instrumentation” libraries that exist for most languages to make setup a breeze. We used auto-instrumentations-node in my project and it was well worth the usage for a team just starting to work with OTel! ([npm package link](https://www.npmjs.com/package/@opentelemetry/auto-instrumentations-node))
Once it’s implemented, Open Telemetry is useful for debugging, maintaining reliable systems, and monitoring performance of your application(s).
## closing thoughts
[Open Telemetry](https://opentelemetry.io/) isn’t necessarily super _easy_ to use, but it was much easier to work with than I expected!

Speakers and Organizers from the Denver Gusto Lightning Talks with Women and Folks in Tech on April 22, 2025 | Left to Right: Mindi Weik, Kseniya Lifanova, Margaret Sabelhaus, Lisa Barcelo, Tori Huang, Alejandra Dominguez, Christine Lee | Second Row: Liz Donovan, Vui Nguyen
It takes some “fiddling” to get it to work properly for your setup and we’ve poured over the data we have since received to begin to understand what we’re capturing and what it means.
There is also a bit of a learning curve getting to know your observability tool(s) of choice.
But, all-in-all, it has been well worth the time investment. Our application is now more resilient and reliable. Just look back at that real world example above! ⬆️
I am still amazed at how helpful OTel has been in identifying not only issues we didn’t know existed, but also digging into the logs and traces in one place so we could follow the steps and pinpoint the root cause.
It feels like our real example was resolved much faster than it would have been had I went through many other steps to debug and figure out the nuanced issue that didn’t appear like an issue locally!
If you haven’t yet, but you want to improve your understanding of your application adn its actions, I highly recommend Open Telemetry! 😁
## 👀 bonus: recording
## what's the difference?!
URL: https://mindiweik.com/blog/whats-the-difference/
Published: 2025-05-13
After trying to get 3 “big” software engineering words to stick, I wrote a similar post:
#### [3 big, scary software engineering words explained](/blog/3-big-scary-software-engineering-words-explained)
My brain often mixes up transpiling and compiling. As I picked up TypeScript, I wasn't always clear on how they affected runtime. I needed to get them straightened out!
What is the best way to solidify that information? Research, write it down, and share it! Maybe my interpretation will help you get more clarity, too.
I previously wrote about two of the items we’re going to talk about today, from the perspective of TypeScript, specifically:
- [Exploring TypeScript: TS Compiler](/blog/exploring-typescript-ts-compiler)
- [Exploring TypeScript: Runtime](/blog/exploring-typescript-runtime)
But today, let’s cover these three topics - transpiler, compiler, and runtime - more broadly. Shall we?
**Here, we’ll cover:**
1. 📖 Compiling
2. 🧑🏽🍳 Transpiling
3. 🔥 Runtime
_Warning, you may get hungry reading this. I was hungry when I wrote it. Ready?_
I was trying to think of a good way to describe and/or remember the differences. I love analogies. And I love to eat. So, let’s talk food.
Hear me out! 🤣
Let’s pretend we’re making something warm. I’m in the mood for some ramen.
We have a recipe in front of us (our code) 📖 that we want to combine and turn it into a delicious bowl of soup (something that will work in a browser or on a computer). 🍜
## 📖 compiling
_First, let’s change the recipe from one language to a completely different language._
So, our recipe is currently written in Japanese (TypeScript), but we need it to be in English (JavaScript) so that other people who don’t understand Japanese can read it and enjoy this delicious ramen recipe, too.
**Compiling is essentially translating code from one language to another!**
_This could also look like translating C or Rust to machine code._
## 🧑🏽🍳 transpiling
_Okay, great! But, now we want to rewrite it to make it simpler English._
Maybe we used words that are too fancy or went into way too much detail about some steps. We care a lot about this ramen!
Maybe someone new to cooking wants to make some, too! But, they want a simpler version. Pretty please?
Let’s rewrite the recipe from those detailed, fancy words (modern JavaScript after we compiled our TypeScript) and reduce that to more basic instructions (older JavaScript). We’re still using English this time, but making it simpler.
**Transpiling transforms code into a simpler version of the same language; it’s easier to understand.**
_This can also look like a transformation of JSX files to JavaScript or Sass to CSS._
## 🔥 runtime
_Finally, let’s actually use this recipe we’ve worked so hard on! We’ll cook the ramen!_
Our computer is following the recipe we’ve made and generating our ramen!
Actually, it’s running your code, but if you had a project that took a ramen recipe input to make an image or something of ramen (in the browser or Node with our JavaScript example), then you will have made ramen in a sense! 🤔
However, it’s important to remember that if something goes wrong (like missing ingredients because they were out at the store or steps out of order because we thought we already did the step before), we may find ourselves in a runtime error. We should handle those!
**Runtime is when a computer is running the code we wrote.**
_This can also look like Python running in the Python interpreter or Java running on the JVM (Java Virtual Machine)._
**🍜 Yay! We finally made it!**
I hope that these breakdowns are helpful.
Are any other terms, concepts, or phrases tripping you up or wasting your precious time Googling/asking AI again because they’re not sticking? Let me know!
## the hidden currency of connection
URL: https://mindiweik.com/blog/the-hidden-currency-of-connection/
Published: 2025-04-15
Whether you are a natural extrovert who thrives in groups and fills up their battery when surrounded by others or a shy introvert who would rather charge up amongst a stack of books or a quiet space in solitude, you _**need**_ networking.
**Here, we’ll cover the…**
1. ❓What
2. 🤨 Why
3. ⏰ When
4. 🗺️ Where
5. 🥸 Who
**…of Networking! Let’s boogie. 🪩**
### want to watch instead? you got it, dude. 👍
## ❓what
Networking isn’t one specific thing. It can be in-person or online, in live conversations or via text. Networking can span multiple participants, too, as with large groups, small groups, or even one-on-one.
> Ultimately, networking is about pushing personal growth and building your own, personal community.
It takes time to cultivate and curate that community. Meeting someone once does not automatically add them to your community. Relationships are fostered over time!
Perhaps to foster a new relationship, you might see someone at a recurring event, follow up after meeting them, or schedule a coffee chat to get to know them better. The more you connect, the more that relationship will grow.
I’m not saying you have to be a best friend to everyone you meet and hang out with all the time. However, you should make an effort to connect with folks you admire, who inspire you, help you grow, or just make you feel good.
_Always remember, if anyone crosses a line or you’re not mutually benefitting, you don’t have to keep engaging. Give yourself permission to disengage._
## 🤨 why
One obvious benefit is that networking can help you when you are in need. For instance, you need help finding a job, funding sources for a startup, or mentor options, for example.
Networking doesn’t have to be - _and really should not be_ - done only when you need something, though. Yes, it takes work, but having a strong network can only help you in the long run. Remember, we’re ultimately building up our community!
If you put in true effort to your network, I can guarantee you will find opportunities you never considered! I’ve been asked to speak, join a few active projects, and organize a local women in tech Meetup group. These opportunities all blossomed through networking!
Most importantly, you might be surprised by the new friendships you find in your community. Especially if you are a remote worker!
I’ve met wonderful people all over the globe, throughout my US state, and within different realms of tech that have blown me away. 🤩
## ⏰ when
**Spoiler Alert: Anytime is a great time to network!**
Networking can happen at the grocery store or a cafe while waiting in line. It can happen at the water cooler in an office or on social channels in your online communication platform, like Slack. It might even happen at an art class or a child’s sporting event!
More than likely, you’re familiar with conferences or organized events. These events are great, often providing at least one similar connection you can use as a springboard, like a Cloud conference or a UI/UX meetup. Many have built-in event structure, helping some folks feel more at ease.
**An important thing I like to remember, particularly for organized events, is to consider what times _actually_ work best for you.**
You want to present your best self, not your completely exhausted or stressed self!
If you are especially nervous or anxious, you may already struggle to get to a networking event. When you’re tired, will you actually push through and go?
For instance:
- With family or child obligations on the weeknights, maybe look for weekend events when you have more support.
- Work during the day? Perhaps an early morning coffee session or run club is best.
- Morning people could struggle with weeknights and should look for earlier events.
- Vice versa, night owls might seek out a later gathering.
**Confession: Sometimes I don’t want to go to an event either, for one reason or another. In those moments, I check in and ask if this event serves me.**
If the answer is no, then I politely decline in an RSVP situation or don’t go if it’s an open event. I’ve also dropped out of events I’ve paid for when I realized it was not for me or I was not in a headspace where it would be worthwhile.
If the answer is yes, I find a way to reframe the situation and go anyway. In these times, I’ve rarely ever left not having enjoyed myself. It’s generally worth the push!
## 🗺️ where
We know that any time can be network-building time, so by extension, you can network from almost anywhere!
That said, I realized in a conversation the other day that getting started is one of the hardest parts of this process. Where should you start?
**I recommend finding safe spaces to start**, especially for those who are new, nervous, or from underrepresented groups. I know it can be scary at first!
It might take a few attempts, so be patient, but look for your people! Whether that’s what you do for work like a product group, or it’s a group for underrepresented folks like Out in Tech or ColorStack for people of color in tech. Maybe instead, it’s a group around something you do on the side, like gardening or gaming. It doesn’t have to be about tech!
Start small; start somewhere where you feel comfortable going and being yourself.
If you become too uncomfortable, like if the group is too large or you’re overwhelmed, give yourself permission to leave.
_Just promise me you’ll try again or try another option!_
Once you start building your connections, it will build your confidence to try new groups and meet more people over time. Your new connections could become new friends, mentors, or even just point you toward another event where you might feel comfortable or welcome!
## 🥸 who
Of course, it can be easier to connect with people doing similar things as you, but **I want to challenge you to** **find connections outside of your own tech stack, industry, or comfort zone.**
When you bring in varied people, you build up your empathy and learn more about other areas, which can improve your problem-solving capabilities. Plus, you’ll likely have someone to call on when you need their expertise!
You should share about yourself, what you’re working on, or what you need help with (like finding a job or identifying startup funding sources). However, find appropriate times for this and don’t hog the conversation.
Consider the Maya Angelou quote:
> "…people will forget what you said, people will forget what you did, but people will never forget how you made them feel."
It is so applicable to networking!
**It’s not about you.** **The key to building bonds lies with others.** If you are asking more questions than you are talking about yourself, you’re probably doing well. 👍
If not, try to stretch your curiosity muscles and ask more about them, their passions, what they are working on, etc.!
## 💭 closing thoughts
Whatever you do for networking, remember to be yourself! Not everyone will like you, and you won’t like everyone, and that’s okay. We don’t need to force it - that only leads to wasted energy.
**Bring yourself and an open mind; I’m sure you’ll find more in networking than you expected!**
### 🥳 bonus
Someone in _my_ _network_ mentioned that they hadn’t been networking for the last several years. They wanted to get back into it and started reading _[Networking for People Who Hate Networking](https://www.thriftbooks.com/w/networking-for-people-who-hate-networking-a-field-guide-for-introverts-the-overwhelmed-and-the-underconnected_devora-zack/311700/)_ by Devora Zack. Although not yet finished, they said there were multiple helpful tips even within the first half!
*_I have not read this book myself. No guarantees of anything!_
## jmeter performance testing: part 2
URL: https://mindiweik.com/blog/jmeter-performance-testing-part-2/
Published: 2025-04-08
I previously shared the beginnings of my journey into JMeter in Part 1:
#### [jmeter performance testing: part 1](/blog/jmeter-performance-testing-part-1)
If you missed that, the TLDR is that JMeter has a nuanced interface that takes practice. We explored JMeter basics and discussed the setup options used for our specific API service that acts as a mapping translation.
Now, we move on to Part 2! ✌️This time, we’ll look at my usage and experience creating a script to parameterize and automate for the future.
**Here, we’ll cover:**
1. 👩🏽💻 Usage and Testing
2. 🤖 Script and Automate
## 👩🏽💻 usage and testing
To get going, I checked how JMeter worked in the GUI with a local server instance and also checked connections to our development service. It was important to me to check that everything worked properly before getting too far.
Then, we defined a plan for our performance testing. We had some example average data from a similar service. In [Part 1](/blog/jmeter-performance-testing-part-1), we used 50,000 daily requests as an average for simplified math. We’ll continue to use this for consistency. 👍
Before officially running the tests, _consider if others will use your service simultaneously_. In our case, other team members were implementing and pinging the service.
I informed others using the service with ample notice of the intended testing start and stop times. With a globally distributed team, the overlap was small. That said, it’s important to inform them so they don’t encounter unexpected issues and to avoid requests our tests aren’t capturing.
**Next, we’ll cover four key usage areas:**
1. Start with a Plan
2. Configure the Tests
3. Test Away
4. Don’t Forget to Document
_Let’s go!_
## start with a plan
We opted to perform **load testing**, **stress testing**, and **spike testing**. To learn more about these in detail, [check this Geeks for Geeks post](https://www.geeksforgeeks.org/performance-testing-software-testing/#types-of-performance-testing:~:text=users%20or%20transactions.-,Types%20of%20performance%20testing,-Performance%20Testing%20is). In the meantime, here are my generalized definitions:
**Load testing** checks the average load we expect to receive to ensure we can manage existing requests.
- We were also provided with the highest average daily requests for our project. For our example, let’s say that it’s 100,000 daily requests.
**Stress testing** pushes those average loads even further to see if we can find a maximum load.
- We opted to test 3x the load for simplicity to start. We discussed going further, but it seemed unrealistic that our load would increase more than this for now.
- \*Our service mainly exposed GET requests, so this high rate without failure is not unusual for simple read operations.
**Spike testing** adds a lot of stress quickly to see if the server can handle it.
- To ensure we weren’t hitting a “cold” server, we implemented a 5-minute “warm-up” period where we sent through the average load before attempting to push its limits.
- We also wanted to ask: If it fails, does it recover? How quickly will it recover?
**Standard Load testing** was recommended by one of our QA team members.
- This is essentially running the average load over 1-2 days to check the system’s long-term strength.
- \*We decided not to, but we could have also tested the highest average load.
- We chose 48 hours for our test.
- I opted to run this toward the end of the week and over a weekend to reduce disruptions for other devs testing out our service for implementation.
## configure the tests
Next, I used the setup options from [Part 1](/blog/jmeter-performance-testing-part-1). Although it was a bit cumbersome, I created a file for each testing scenario. I did this for two reasons.
1. I could work in a second JMeter instance to prepare the next test file while the current test runs without interruption. Using the same file and then saving a new version with the latest configurations saved a lot of time, ultimately.
2. I didn’t want to get my wires crossed with different configurations for each test scenario. With these pre-configured, I could revisit them when needed… Which came in handy when my internet became unstable during development testing.
_**“So, what were the configurations?”**_ you ask. Let’s cover some rough variations!
### 🔁 option 1: http request
For each test, this remained generally the same. The only difference was the local http:// request as opposed to the deployment service https:// request.
### 📋 option 2: http header manager
Another easy one! This remained constant between the sets of local and development service tests, but we used separate values for each environment.
### 🧐 option 4: extended csv data set config
Yes, I went out of order; bear with me! This was another consistent option. This setup used the same CSV file with thousands of inputs.
### ⏱️ option 3: constant throughput timer
Here’s where most of the complications came in. Each test had a different throughput amount and/or time range we wanted to test. Below are the details. Remember that the throughput field needs a number for the “samples per minute.”
Again we assume we had an average of 50,000 daily requests with the highest average of 100,000 daily requests for easier reference. I’ve rounded all results for simplification, but you can extend to whatever decimal point range you like.
#### **load testing**
- Average Load Testing
- **Time:** We wanted to test the average load over an hour.
- **Throughput:** 50,000 / 24 hours / 60 minutes = **34.7 requests/minute**
- Highest Average Load Testing
- **Time:** We wanted to test the highest average load over an hour for consistency.
- **Throughput:** 100,000 / 24 hours / 60 minutes = **69.4 requests/minute**
#### **stress testing**
- 3x the Average Load
- **Time:** For consistency, we maintained an hour timeframe.
- **Throughput:** Multiply the Average Load Test amount by 3. I’m using the rounded number to keep things simple.
- 34.7 requests x 3 = **104.1 requests/minute**
- 3x the Highest Average Load
- **Time:** For consistency, we maintained an hour timeframe.
- **Throughput:** Multiply the Highest Average Load Test amount by 3. I’m using the rounded number again to keep things simple.
- 69.4 requests x 3 = **208.2 requests/minute**
#### **spike testing**
We set up a short “warm-up” period for the server to better simulate a real-world spike among regular traffic in each spike test. Each of my warm-ups lasted 5 minutes and used the average load throughput of **34.7 requests/minute**.
It’s worth noting that adding a warm-up period requires an additional thread to set up different throughput and test details.

For consistency, each spike test was set to last for 10 minutes after the 5-minute warm-up. Below are three options I chose, though you may make different choices based on your expected traffic load or how hard you want to test the system.
- 3,000 requests
- **Throughput:** To simulate this spike, we will divide our spike amount of 3,000 by the 10-minute timeframe.
- 3,000 requests / 10 minutes = **300 requests/minute**
- 10,000 requests
- **Throughput:** To simulate this spike, we will divide our spike amount of 10,000 by the 10-minute timeframe.
- 10,000 requests / 10 minutes = **1,000 requests/minute**
- 50,000 requests
- **Throughput:** To simulate this spike, we will divide our spike amount of 50,000 by the 10-minute timeframe.
- 50,000 requests / 10 minutes = **5,000 requests/minute**
#### **standard load testing**
After performing all of these, I checked with our QA testing expert to see if I had missed anything. She suggested we perform this test, too! I repeated the average load and highest average load tests with a much longer timeframe.
- Standard Average Load Testing
- **Time:** We wanted to test the standard average load over 2 days or 48 hours.
- **Throughput:** 50,000 / 24 hours / 60 minutes = **34.7 requests/minute**
- Standard Highest Average Load Testing
- **Time:** We wanted to test the standard highest average load over 2 days or 48 hours.
- **Throughput:** 100,000 / 24 hours / 60 minutes = **69.4 requests/minute**
This is quite a long time to test! In our case, we had others using the API to implement it in the platform to ultimately use the service. For this reason, I tested Friday through Sunday. I checked in a little over the weekend, which is easy to do when you work remotely. 👍
## test away
Once everything is set up, run the tests!
I kept an eye on them and did low-key tasks while waiting to check the progress in case of any issues. For instance, my internet cut out during one of my tests! I quickly reset and started the test again.
## don’t forget to document
Immediately after finishing the tests, I documented results, stored the test files, and gathered details so anyone could access and run the tests later if desired. Don’t wait!
I added this to a Confluence document with additional information, like which plugin(s) would be needed.
Documentation like this, even if it’s not exciting, will be helpful for your team and your leaders to know what you tested so they can provide feedback and understand what was tested if more testing is needed later.
## 🤖 script and automate
Despite all that lovely documentation, my teammate and I felt storing test files in the repo would be best for our future selves and teammates.
What would be even better? If our future selves and teammates could run those tests with a simple script, all the better! Let’s talk about implementing automated testing capabilities using parameters.
_This is where the real fun began!_
## user defined variables
The first thing to learn was how to use the [CLI]() and incorporate User Defined Variables. This [Stack Overflow response](https://stackoverflow.com/questions/59139762/how-to-use-command-line-parameters-in-jmeter#:~:text=10-,Let%27s%20start%20clean%3A,-In%20the%20User) helped get me started.
Again, I toyed with different options and worked through some examples to get comfortable with User Defined Variables. It’s neat to see just how much you can parameterize! _Spoiler alert: almost everything!_
### how to parameterize?
First, we need to add a config element for User Defined Variables.

Then, we can add in our key-value pairs. Here’s an example using a key of example-key and a value of example-value.

Then, these variables can be plugged into various fields throughout the test! Let’s look at a simple example. We will use the following snippet to introduce our User Defined Variables as a parameterized value:
```js
${__P(example-variable,default)}
```
The left side within the parentheses is our User Defined Variable name (x-api-key below), and the right is our default value. In the case below, we use foobar. Meaning, if we provide no value, foobar will be used as the value for this header.

One important thing I learned during this process is that it is possible to use the full URL in the path instead of setting the protocol, the server name/IP address, and identifying the “Advanced Implementation” setting [described here in Part 1](/blog/jmeter-performance-testing-part-1).

## consider the options
Once I had thoroughly tested the available parameterization options, I had to consider our goals. I first went overboard and made a single test file that we could run any test we wanted because everything was parameterized, with most of the test numbers defaulting to zero as a safe placeholder.
We discussed this and decided on the best options to parameterize, limiting our files to 3 in total instead of one for every single test scenario. **Here’s a sample of the script:**
```bash
jmeter -n -t \
-Jurl= \
-Jx-api-key= \
-Jcsv= \
-f -l
```
**What's happening here?**
**-n** - This argument tells JMeter to run in CLI mode
**-t** - This indicates that the next argument provides a path for the .jmx test file
**-Jkey=value** - The **url**, **x-api-key**, and **csv** keys were defined within the test plan to accept user input
- I made the **url** value required! The protocol is necessary to include.
- For example, use http://127.0.0.1:8080 for local testing or https://url-path.com for external testing.
- I added specific instructions not to include the path and queries for the URL, as I configured this for randomized testing in the JMeter test plan.
- **x-api-key** was optional. The default value was foobar as mentioned above.
- **csv** accepts a file with options we discussed in [Part 1](/blog/jmeter-performance-testing-part-1).
**-f** - This argument tells JMeter to force delete existing results files and web report folder if present before starting the test as a cleanup step
**-l** - This indicates that the next argument provides a path to store the test results in either a .jtl or .csv file
Once a test is completed, you can review the results! Using .csv is an option, but it provides minimal detail. The most informative option uses JMeter GUI mode to review the .jtl results file using the View Results Tree, Summary Report, or Aggregate Report options within the test plan.
## create and iterate
I started by creating the three files.
#### load test
I created a load.jmx file, which contained the default settings for a 60-minute test of the average load.
#### stress test
The stress.jmx file contained the default settings for a 60-minute test of 3x the average load.
#### spike test
The spike.jmx file contained the default settings for a 10-minute stress test (50k spike target by default). As with spike testing, there is a 5-minute warm-up before the spike start for a 15-minute total test.
Then, I tested the files thoroughly with all input options I had parameterized to confirm they worked as expected!
Finally, I added these files to the repo and added information on a sample script and how to use it, similar to what I shared above.
**I hope that this has been helpful and/or interesting! This was where the fun presented itself in the form of a challenge; it was a joy trying to figure out the parameterization!**
## 📚 further reading
- Stack Overflow: [How to Use Command Line Parameters in JMeter](https://stackoverflow.com/questions/59139762/how-to-use-command-line-parameters-in-jmeter)
- Perforce: [BlazeMeter - User Defined Variables](https://portal.perforce.com/s/article/Using-User-Defined-Variables-1707509382889)
- BlazeMeter has so much more to learn. A reader [suggested this free course](https://university.blazemeter.com/learn/course/external/view/elearning/485/apache-jmeter-intro) to guide you from introduction through running the test and analyzing the results.
- It's possible to get free completion certification at the end!
## opportunity is knocking
URL: https://mindiweik.com/blog/opportunity-is-knocking/
Published: 2025-03-11
At times, our professional growth may feel slow or even halt. We lack new projects, or maybe tasks feel repetitive. We might feel useless or bored.
**We needn’t stay that way!**
When I started feeling something along these lines, I decided to take action rather than dwell in discomfort. A lack of obvious opportunities can’t stop me.
> "Obstacles don't have to stop you. If you run into a wall, don't turn around and give up. Figure out how to climb it, go through it, or work around it.”
>
> Michael Jordan
Growth takes effort, intentionality, and sometimes a dash of creativity! I started asking more questions, both of myself and to those around me, to brainstorm ideas to put into practice.
> For everything I’ve shared, please remember that we should be **respectful of others**. This is especially true when people are likely going out of their way for us!
Before asking for help, we should consider what we need and be as specific as possible. If they say no, honor that and check another door.
**Here, we’ll cover:**
1. 🫵 Look within your own team
2. 🤝 Don’t underestimate other departments
3. 🤔 Reach outside your company
4. 😊 Work on your own project(s)
## 🫵 look within your own team
Whether it’s a direct teammate or an admirable person on another team, start within! Don’t jump in “half-cocked,” though.
**Some ideas I started with to identify potential mentors or contacts internally:**
- **What to learn first?** Figure this out if it’s not already known. This can help pinpoint who to ask for advice or indicate what we need once we connect.
- **Consider the “quiet” folks within!** Some are uncomfortable in large groups, but have ample knowledge to share. They may thrive in a more personal environment; these people are hidden gems who make great mentors.
- **Ask management for ideas.** If you’re newer to the company or you’re struggling to identify someone to connect with, they may have better insight into who is best in certain areas we may not have considered before. They also often know who has bandwidth to help.
- **Have you asked to help with a new project?** Not always, but sometimes having the conversation is enough to inform your manager you’re bored, burnt out, or want to grow in a different area. They’re supposed to be on your side and will likely do what they can to help!
Once internal help is identified, think before connecting. Especially if the person(s) we’ve found are more senior, we do _not_ want to waste anyone’s time. It may be the only chance we get to talk with them! Plan questions to ask or concepts to learn before reaching out. It’s helpful to them if we share a bit about what we want to learn and why.
The more clear the questions we bring, the better the experience for all!
**Here are a few examples:**
- Hi [name]! 👋 I was working on X project and noticed you were involved in the early design stages. If you have time, would you be willing to share how some decisions were made and what kind of constraints there were? I’m interested in learning about [X-related topic] and I’d appreciate hearing your experience.
- Hello, [name] 😊 - We haven’t yet had the opportunity to connect! I admire the way you handled Y solution. Do you have some time to chat sometime? I want to grow in this area and I believe your insights would be so helpful!
- Hi [name], my name is [Mindi] and I work on the Z team. [Manager] suggested I reach out because you are great with XYZ tool. I’m looking to better understand how it interacts with our platform. Is this something you can help me with?
Genuine acknowledgements or willingness to learn will often catch their interest. If at all possible, try to add in some way that it might be beneficial to them, too.
Perhaps the Engineering Manager shared that their recommendation is actively looking for opportunities to mentor. We can tie that into our request. Or maybe if we learn from this person, we could take this smaller task off their plate while we grow.
The benefit to them will depend on the situation.
## 🤝 don’t underestimate other departments
Unless an organization has strict policies barring collaboration across departments (Is that a thing? 🤔), connecting with new people working in other areas can bring a wealth of insight and information.
**🚨 Take care! We should be helpful - and respectful - of their time. Be specific!**
One-on-ones don’t just have to be between managers and their team! We can connect just to discuss what’s at hand in a one-on-one atmosphere, possibly even more than once or on some regular cadence. We could also try connecting over lunch or coffee, in-person or virtually.
**Some scenarios to consider include:**
- Connect with others in **support roles** who work with customers that touch, or are adjacent to, something we’re building.
- Talk with someone in **sales or marketing** to better understand a user’s mindset when they purchase our company’s service.
- Locate a **manager-level person** to ask for advice about their department, or maybe whether leadership is something we want to pursue from another perspective.
Whomever we talk with, we will almost certainly learn something new! Often I am surprised at what I learn when asking more questions or hearing about what others do in their day-to-day. I’ve even collaborated with them to make something easier for one or both of us!
Even if your organization doesn’t openly support this approach, the benefits can far outweigh concerns for lost time or efficiency. Connecting with others expands our understanding of the overall organization, improves our empathy, and helps us think more creatively.
Sometimes asking for forgiveness rather than permission isn’t a bad approach 😉 but use your best judgement!
## 🤔 reach outside your company
Whether you find someone internally or not, there is almost always someone in your network who can help! Or, someone within your network can connect you with someone in _their_ network. It’s all a big web after all. 🕸️
Remain considerate of others and their time. As you share more about your goals and desires, ideally you have a strong idea of what you want to accomplish to guide you to the right connections.
If your idea isn't entirely solidified, perhaps they can connect you with someone they know in the domain or working with a similar technology that can help you pinpoint what you're working toward learning. The more you know about your goals, the better the connection could help you get closer to those goals.
There are several ways you can reach outside your network. If you start sharing more, you might be surprised who you or your network will find!
Here are just a few examples. Think outside the box; you might find other avenues that work better for you!
**In-person ideas:**
- **Talk to people you interact with frequently.** This might be family members, friends, neighbors, acquaintances at the grocery store, clubs, your child's school, or other similar places where you interact with other humans.
- **Attend local events.** These events can be tech-related but don't have to be. As you connect with people outside your normal domain, you might generate new ideas or find connections you had never considered.
- **Join hackathons.** If you’re lucky enough to have a hackathon in your area, I recommend attending! I’m on the lookout for some myself.
**Virtual ideas:**
- **Leverage social media.** I lean toward [LinkedIn](https://www.linkedin.com/in/mindiweik/) because it's the professional option, but reaching out to folks in your network on other platforms, too, couldn’t hurt. With LinkedIn, you also have an insight advantage, knowing what tech they work with or where they work.
- **Attend virtual events** - Similar to in-person events, these can be tech-related but don't have to be. There are loads of webinars and networking events online to expand your opportunities.
- **Open Source** - If you have the time and capacity, consider working on open source projects to expand your skills!
## 😊 work on your own project(s)
Whether you have a cool idea or find a cool way to play with something new, working on personal project(s) is yet another great option.
> "Don't sit down and wait for the opportunities to come. Get up and make them.”
>
> Madam CJ Walker
If you’re solely focused on learning, you can have a great time tinkering with new technology, patterns, languages, etc. If no one looks over your shoulder, the world is your oyster; experiment away!
At the same time, this can be tricky. When you work alone, you can gain some odd habits or you may not learn “best practice” techniques. If possible, share your work with someone you like and trust so you can talk through it, discover what you learned, and possibly get some insights from this other person’s perspective.
Better yet, find a friend or two or more to work on a project with. You’ll have camaraderie, multiple brains to pool knowledge to share, and hopefully have fun in the process!
Whatever path (or paths) you choose, there’s opportunity for you waiting behind a door. **_You just have to start opening them!_ 🚪**
## 🍀 still no luck?
I recognize that the above won’t always work or it might just take too much time. It’s worth noting if it is time to [consider leaving your job](https://open.substack.com/pub/thehustlingengineer/p/6-clear-signs-its-time-to-quit-your?utm_source=share&utm_medium=android&r=29u7hv). In several places I’ve come across the reminder that stagnation is a career killer.
Keep learning, keep growing, and challenge yourself. Onward and upward, my friend!
**PS I would love to hear if you try any of these and how they went for you!**
## jmeter performance testing: part 1
URL: https://mindiweik.com/blog/jmeter-performance-testing-part-1/
Published: 2025-03-04
Recently my team deployed an API service. It’s a small mapping solution that will help translate specific data from one service to another. There are a few endpoints, but one core `GET` request is used from this API.
Once deployed, it was up to me to do the performance testing! (I have a recent [LinkedIn post](https://www.linkedin.com/posts/mindiweik_k6-jmeter-apidevelopment-activity-7282911511675580416-uBPa?utm_source=share&utm_medium=member_desktop) about it. 😊)
Our wider team already uses [JMeter](https://jmeter.apache.org/) to test other internal services, so we opted to use this tool to ensure consistency across the organization. Besides, it is still a popular tool from what I can tell.
I dove so deeply into JMeter that I’d like to split this into two parts! ✌️
First, we’ll review some initial understanding and setup details. In the [second part](/blog/jmeter-performance-testing-part-2), we’ll look more at usage and my experience creating a script to parameterize and automate for the future!
**Here, we’ll cover:**
1. ☝️ Quick Intro to JMeter
2. 🛠️ Test Setup
## ☝️ quick intro to jmeter
[Apache JMeter](https://jmeter.apache.org/) is a free, open-source Java application for performance testing at the protocol level. It offers flexibility and configurability. It’s even OS-independent!
My initial research indicates that it’s still popular. I examined other options and asked other testers for their feedback and experiences. JMeter is primarily used for web application testing, however, it can also test APIs, databases, and more. It’s powerful enough for a wide range of testing capabilities. _My experience focused on API testing._
\*It doesn’t fit all use cases.
JMeter can be used through a Graphic User Interface (GUI) or Command Line Interface (CLI) commands. We’ll cover the latter in the [second part](/blog/jmeter-performance-testing-part-2).
> Fair warning, JMeter’s GUI looks outdated! It was clunky at first, but once I got the basics down it was easy enough to manage and navigate. Perservere! 👍
At the core, JMeter facilitates sending requests to your server and fielding responses. It then captures the data, which you can use to generate reports and review results. Results can be generated in multiple file formats, such as XML, HTML, JSON, and text.
JMeter at a glance
01 · request
JMeter sends a Request to the Server
→
02 · response
The Server returns a Response
→
03 · reports
JMeter generates Reports
## 🛠️ test setup
The initial setup and getting to know the software is nuanced, to say the least. A [Geeks for Geeks tutorial](https://www.geeksforgeeks.org/how-to-use-jmeter-for-performance-and-load-testing/) helped me get the basics, which was _very_ useful. Afterward, I better understood where things were and how to leverage more options.
To start, I set up a simple test with four listeners: the Backend Listener, the View Results Tree, the Summary Report, and the Aggregate Report. These are used to view results and reporting. I suggest looking around at these before, during, and after tests using the GUI while getting set up to get a sense of what you might need from them.
🚨 _Check whether you want your Listeners under the Test Plan or in nested elements added below! I wanted a snapshot of the overall so I added mine to the Test Plan._

Next, I started working on specific options we wanted to use. Trial, error, and research ensued to determine how to accomplish our goals.
For example, we wanted to test real requests with randomized inputs using a CSV with thousands of possibilities. I found a [CSV Data Set Config](https://www.blazemeter.com/blog/jmeter-csv-dataset-config) option, but this inputs from the file line-by-line! Random inputs would better simulate a real-world test. Identifying that tool took more effort, but I did find it! We’ll cover that below.
### 🔁 option 1: http request
Testing an API service requires using an HTTP Request. Simple enough! Right?
Yes, generally. I added a name for the Request (optional) and the URL, path, and port number. This was pretty easy for local testing, but I learned that some changes are needed to access external services and resources via HTTPS requests!


For an HTTPS request, identify the protocol in the protocol field and - likely - set the port number to `443` to access your SSL/TLS-secured resource.
Additionally, under the “Advanced” tab I needed to update the “Implementation” drop-down to the “HttpClient4” option. Later, I learned that having this option selected for HTTP request testing didn’t change anything, so I suggest this in all cases.

One “gotcha” you might encounter is that you’ll need to ensure you have a Thread Group first and then add associated options under that particular Thread Group. We’ll cover this below.

### 📋 option 2: http header manager
For our service, we use a specific header. This needed to be included for our service to work properly with the tests, and this was one of the simplest items to add. I popped in a header key and an appropriate value and checked this off the list. ✅

### ⏱️ option 3: constant throughput timer
While performing the initial simple tests to grasp JMeter, I discovered requests are sent without a throttle by default. However, we wanted to test specific loads that represented an approximate average of what we saw in the past for a similar service.
To achieve this, I used this option to provide a “Target throughput (in samples per minute).” Let’s examine that further. Say we were expecting 50,000 requests on average for the entire day for simplicity.
```
50,000 / 24 hours / 60 minutes = 34.7 requests/min (simplified, rounded)
```
In our example, I input 34.7 to the “Target throughput (in samples per minute)” field which would spread requests across the time frame provided to match this throughput. You can be more specific on the decimal, if desired.

In other words, if I test for 1 minute, I should expect about 34 requests to have been sent and received for my test’s duration. This option was helpful for deeper control over our test parameters!
### 🧐 option 4: extended csv data set config
Finally, one of the most interesting options was the [Extended CSV Data Set Config](https://rollno748.medium.com/extended-csv-dataset-config-for-jmeter-17b1d8bda6b8). The CSV Data Set Config option didn’t provide the desired random input effect.
To use this, however, I needed to install the plugin. This was simple enough.
**Plugin Directions:** Access plugins by selecting “Options” in the Menu Bar, then choose “_**Plugins Manager**_.”

A window should open for the “_**Plugins Manager**_.” Select the “_**Available Plugins**_” tab and search for the plugin by name. Once identified, mark the checkbox and choose “_**Apply Changes and Restart JMeter**_” in the lower right corner.

From here, I reviewed/updated the Filename, Variable Name(s), Consider first line as Variable Name, Select Row, and Sharing Mode fields.


1. **Filename -** This was simple enough; I provided the path to my CSV file with the thousands of input options.
2. **Variable Name(s) -** In our case, our CSV had 2 labels in the header row that aligned with our request parameters. Let’s call them “option1” and “option2” for our example.
3. **Consider first line as Variable Name -** As mentioned, our CSV has a header row. Therefore, I left this set to true.
4. **Select Row -** [This resource](https://rollno748.medium.com/extended-csv-dataset-config-for-jmeter-17b1d8bda6b8#:~:text=using%20this%20plugin-,1.%20Select%20Row,-This%20selection%20allows) describes the options clearly. My goal was random and I chose this in the drop-down.
5. **Sharing Mode -** The options are “All threads,” “Current thread,” and “Current thread group.” I left the default “All threads” selected.
Now we’ve got a setup that replicates my selections!
> **👋 That’s it for now! I hope you’ll check out Part 2 where we cover the usage of JMeter followed by parameterizing the file to script and automate for future use!**
#### [jmeter performance testing: part 2](/blog/jmeter-performance-testing-part-2)
## further reading
- BlazeMeter: [JMeter Testing: Everything You Need to Know](https://www.blazemeter.com/resources/jmeter-testing)
- PS I found many helpful resources on BlazeMeter!
- Radview: [What is JMeter?](https://www.radview.com/glossary/what-is-jmeter/#:~:text=Creates%20and%20sends%20requests%20to,XML%2C%20JSON%2C%20or%20text.)
- Medium: [Extended-CSV dataset config for JMeter](https://rollno748.medium.com/extended-csv-dataset-config-for-jmeter-17b1d8bda6b8#:~:text=using%20this%20plugin-,1.%20Select%20Row,-This%20selection%20allows)
## 3 habits that helped me recover from burnout and stay motivated
URL: https://mindiweik.com/blog/3-habits-that-helped-me-recover-from-burnout-and-stay-motivated/
Published: 2025-02-25
## icymi I had the pleasure of writing a guest spot...
...for my friend, David Weiss' newsletter called [Besides Code](https://www.besidescode.com/)! It was enjoyable to be a little vulnerable. I hope you'll check out his newsletter if you're not already following it - he shares excellent tips, advice, and stories to help with growth, communication, and leadership skills. 😁
[Mindi Weik](https://www.linkedin.com/in/mindiweik/)
### [link to original post](https://www.besidescode.com/p/3-habits-that-helped-me-recover-from?r=29u7hv&utm_campaign=post&utm_medium=web)
_I’m happy to share this guest post from my friend,_ [Mindi Weik](https://www.linkedin.com/in/mindiweik/)_. She has a vulnerable story to share about burnout that we can all learn from._
_Your mental health matters. And I’ll never stop saying that. I’ve learned this lesson the hard way in my career. I’ve suffered from burnout and high-stress levels more times than I can count._
_In this post, Mindi shares three effective ways to foster mental wellness so you can stay focused on your goals and prevent burnout._
We’re human; we stumble.
Whether a resolution, intention, or goal, it can be hard to maintain momentum. You might grow exhausted or become derailed.
Whatever the case, start by reminding yourself that stumbling is OK. We’re here to talk about ways to pick yourself back up when that happens!
## **shifting**
Last year, I stumbled. More than once. Despite this, I tried to maintain multiple spinning plates. This led to severe burnout.
If you’re unfamiliar with “spinning plates,” it’s a balancing act, somewhat akin to juggling. Imagine someone holding a small wooden pole, precariously balancing a spinning plate. This feat takes work and attention, but it can be done.
Now imagine that person balancing another pole, another spinning plate. This is harder.
What if they held _several_ poles and plates? More plates means the plates are more likely to fall or stop. We only have two hands!
Burned out, all of my plates suffered. Quality dipped, and it was perpetually harder to keep them spinning. You can probably see where this spiral leads.
**When we notice that we’re not performing at our best we can:**
- Drop certain plates
- Take a break
### **drop certain plates**
Dropping all the plates is the worst-case scenario. When you drop everything, you stop, and growth halts.
It’s possible to be forced here by unexpected circumstances. But, if we see the metaphorical train coming, we can jump off the track!
When we do nothing to address oncoming burnout, all the plates fall. They likely smash to pieces unsalvageable. We can catch ourselves and consider other options to avoid losing all of our plates.
The best way to do this is to pause and evaluate each plate. To be most effective, be honest with yourself.
- Do I need to do this right now? Is this a self-imposed pressure?
- What’s the worst that could happen if I drop this plate?
- What’s the best that could happen if I drop this plate?
- Am I spinning this plate for myself or someone else?
- What do I _want_ to accomplish?
Compare the plates. Is our hands-on side project work the most important? Or is time better spent working to improve effectiveness at work?
The individual decision varies, but it’s important to examine thoroughly to identify if you spend your time where you desire or need to. Then you can drop plates that don’t help you progress or are okay to drop. Like a plastic plate that can easily be picked up later, maybe it’s reading a technical book or a personal project you can pause and resume in a few weeks or months.
### **take a break**
If you get anywhere close to burnout, it’s time for a break. Full stop.
This was difficult for me. I worked and learned during the day; during nights, weekends, and breaks in the workday I would:
- build and learn more things
- write [Substack](https://codeoutloud.substack.com/) articles
- engage with my [LinkedIn](https://linkedin.com/in/mindiweik) network
- organize, promote, and host women in tech events
- read informative books or articles
- work on conference or low-key talks
- …and handfuls of similar things plus balancing time with friends, family, and myself.
☝️ This is a list with too many plates.
To pinpoint what to drop, I paused everything for roughly 2-3 months. I found opportunities to step away from work, take a trip, enjoy a staycation, and find quiet space after my workday.
This space helped me think about what I _wanted_ to accomplish and which plates might be plastic. I recharged, ending with a better sense of what was important for me moving forward.
## **start small**
We shifted; we’re ready to regain motivation and resume growth! But how can we do that?
> "If you can get one percent better each day for one year, you'll end up thirty-seven times better by the time you're done.”
>
> James Clear, Atomic Habits
“Atomic Habits” is one of my favorite reads from last year. Much of its advice feels like common sense. However, that advice is often forgotten. We rarely enact this common sense.
The two concepts that helped me most:
1. The 4 laws of behavior change
2. Habit Stacking
### **the 4 laws of behavior change**
Reflect on your goals, resolutions, or intentions and brainstorm ideas to make them slightly easier to accomplish. The more you break it down, the easier to start building momentum, however microscopic it may feel.
The 4 laws of behavior change are:
1. **Cue:** Make it obvious
2. **Craving:** Make it attractive
3. **Response:** Make it easy
4. **Reward:** Make it satisfying
Let’s use a common resolution to start an exercise habit to gain measurable strength or lose a certain amount of weight.
**Cue:** We might use an alarm or do this during lunch. Find a consistent way to make it obvious that it is time to head to the gym or outside for exercise!
**Craving:** We may feel energized to burn some energy. Perhaps if we exercise at lunch we feel a craving to blow off some steam.
**Response:** For a morning alarm, this might be prepping workout clothes near the bed. If we choose lunch and work remote, it may be easiest to step outside, put on a video, or have home equipment rather than heading to another location.
**Reward:** We are relaxed and enjoy the endorphins we released!
Even if we only make it to the gym at first, roll out a yoga mat, or put on some shoes, this is a small step in the right direction. Do a few minutes next, then a few more. Quickly your habit begins to form. Starting is the hardest part; don’t be too hard on yourself in the beginning and allow organic growth over time.
→ Bonus tip: Reverse the 4 laws of change to break a bad habit!
### **habit stacking**
This is one of my favorite concepts because it worked particularly well for me. Building on an existing habit makes a new habit stickier.
I struggled to be consistent falling asleep “on time” for a restful night and an energized morning. I hit snooze on repeat and groggily rushed through a chaotic morning to get to work.
Of course, I still miss the mark sometimes, but it was easier when I built upon my nightly ritual. Instead of brushing my teeth and looking at my phone until I eventually drifted off, I replaced my phone with a physical book.
It keeps me from unnecessary scrolling and promotes my reading habit. Brushing my teeth now triggers an urge to open a book. In turn, I make a little progress in the book and it helps me drift off more easily than my screen!
## **give yourself grace**
There will be bumps and stumbles. It’s normal! The best thing we can do is remain kind to ourselves to keep going when it’s tough.
Self-talk matters. **Talk to yourself like you would a friend.**
### **affirmations and positive self-talk**
I’ll leave you with some helpful ideas. We can reframe our self-talk. I like to imagine I’m talking to someone else in my shoes. It helps me provide more genuine self-talk.
Here are some examples:
- We wouldn’t tell a good friend that they are foolish to start a new project or bad at a new hobby right after starting, right?
- **What we can say instead:** “I am learning a new craft. I will get better with time.”
- We wouldn’t tell our good friend they are terrible because they made a simple mistake.
- **What we can say instead:** “I am allowed to make mistakes, we all do! They are growth opportunities.”
- We also wouldn’t tell a friend that they’re not smart or capable enough to understand a problem.
- **What we can say instead:** “I don’t have to know everything right away. I can break it down step by step.”
_Thank you to_ Mindi Weik _for sharing her story about overcoming burnout and three ways to prioritize your mental health. Please check out her newsletter,_ [codeOutLoud](https://open.substack.com/pub/codeoutloud)_, and [follow her on LinkedIn](https://www.linkedin.com/in/mindiweik/)._
## exploring typescript: primitive types
URL: https://mindiweik.com/blog/exploring-typescript-primitive-types/
Published: 2025-01-14
This is part of a semi-monthly series that will put TypeScript under a microscope to become more adept overall. 🔬
Understanding the nitty gritty bits and pieces of a language can only benefit us as software builders!
**This post will cover basic primitive types within TypeScript.**
1. 🤔 What are primitive types?
2. 📚 Resources for further reading
**Let's dive in!**
You're probably thinking, "Why are we going so low-level and foundational?"
Now that we've covered the [TypeScript Compiler](/blog/exploring-typescript-ts-compiler) and [Runtime](/blog/exploring-typescript-runtime), primitive types are a natural next step in continuing to build upon a strong foundation. The stronger the base knowledge, the better we can grow toward more advanced topics.
So, let's take it from the top!
## 🤔 what are primitive types?
To start, we need a shared understanding of the types we will discuss. Because TypeScript is a superset of JavaScript, these types will naturally mirror some JavaScript.
A few differences exist between JavaScript primitives and TypeScript primitives, mainly relating to the types themselves because TS is a superset of JS. The [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/basic-types.html) is your best source if you have questions about TypeScript primitives!
**What is a "primitive" in the sense of programming?**
Consider a primitive as a very low-level and built-in aspect of a language. It describes the specific data type of one specific value. They are also what the built-in `typeof` operator might return in some cases.
These types are immutable. They can be assigned to a variable, and a variable can have its value reassigned, but that does not change the type of the initial value itself.
> In [JavaScript](https://developer.mozilla.org/en-US/docs/Glossary/JavaScript), a **primitive** (primitive value, primitive data type) is data that is not an [object](https://developer.mozilla.org/en-US/docs/Glossary/Object) and has no [methods](https://developer.mozilla.org/en-US/docs/Glossary/Method) or [properties](https://developer.mozilla.org/en-US/docs/Glossary/Property/JavaScript).
>
> - [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Glossary/Primitive)
Let's take a look at the primitives that span across both JavaScript and TypeScript:
- `string`
- `number`
- `boolean`
- `bigint`
- `symbol`
- `null` and `undefined`
As a note, you may notice that these types are referred to in lowercase (`string`, `number`, `boolean`). If you see them referred to in uppercase (`String`, `Number`, `Boolean`), these refer to the built-in types that contain the methods and properties of the built-in objects for these types. You can [learn more from MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)!
## 🧵 string
The `string` type is one of the three most common primitives. It represents data in the form of text. In TypeScript, you can declare the string type using the `string` keyword, as you might expect:
```ts
let name: string = 'Mindi';
```
You'll notice that single quotes are used in my above example (`'`). Double quotes (`"`) may also be used, as well as backticks (`` ` ``) for a template literal. These are the same options as in JavaScript, just with the added type declaration. Here are examples using these last two options together:
```ts
let dog: string = 'Rigby';
const dogCommand: string = `${dog}, please sit.`;
console.log(dogCommand); // Output: "Rigby, please sit."
dog = 'Rayla';
console.log(dogCommand); // Output: "Rayla, please sit."
```
If you weren't aware, TypeScript is also smart enough to infer types! However, this can come with confusion. In a function, TypeScript would understand that the output will be a string:
```ts
const greeting = (str: string) => {
return str;
};
console.log(greeting('Hello')); // Output: "Hello"
// The type when you hover over `greeting` shows a string type
```
However, if you were to use `const` variable declaration for a string without declaring it as a string more broadly, TypeScript may infer this as a type literal! Take a look at this example. The variable `test` is the literal string of `"Hello"` as its type, not a more generic `string`!
```ts
const test = 'Hello';
// Hovering over `test` in the VSCode IDE shows:
// const test: "Hello"
```
Why is this? When we use `const` in this way, we essentially make the variable read-only. The variables are unable to be reassigned and, therefore, remain constant. If we cannot reassign `test` to, say, `"Goodbye"` then it makes some sense that its type would be `"Hello"` as the type literal as opposed to a `string`.
Although it seems rather simple, I have found nuance is littered throughout TypeScript. Let's keep this in mind as we examine the remaining primitive types.
_**When in doubt, hover your mouse in the IDE to check it out! 👀**_
## #️⃣ number
Another of the three most common primitive types is `number`. As you may have assumed, this type represents number data! These can be integers or floating-point numbers that may or may not contain decimals.
As with the `string` type, you can declare a variable as a `number` with the corresponding keyword:
```ts
let year: number = 2050;
let distance: number = 21.555;
```
As with strings, TypeScript will attempt to infer the type if you do not state it:
```ts
let age = 100;
// hovering over age, shows a type of number
const zero = 0;
// hovering over zero shows a type literal of 0
```
As you can see from the variable `zero`, using `const` will again cause TypeScript to infer a type literal of `0` as opposed to the more broad `number` type.
_**When in doubt, check it out! 👀**_
## ⚖️ boolean
The `boolean` type is the third most common primitive. This refers to the two logical values of `true` and `false`.
They are typically used in conditional testing like `if...else` or `while` statements that return a truth or false result. You can also use the ternary operator; here's an example:
```ts
let happy = 'happy';
let isHappy: boolean = happy === 'happy' ? true : false;
// hovering over isHappy shows a type of boolean
```
And, if you were wondering, a type literal will occur if using `const` as with the `number` and `string` primitives.
```ts
const isTrue = true;
// hovering over isTrue shows a type literal of true
```
_**When in doubt, check it out! 👀**_
## 🏋 bigint
The `bigint` primitive is similar to `number`, but the value is too large to be represented by a typical number.
There are subtle differences between `number` and `bigint`. First, the built-in `Math` objects cannot be used on a `bigint` value. Additionally, `bigint` values cannot mix with `number` values in operations. These number types must be coerced to the same type first. Coercion to a `number` value, however, can cause degraded `bigint` precision.
Overall, this primitive is rarely used in my own experience. My work and side projects thus far haven't dealt with such large numbers! If you have a great real-world example, I'd love to hear more about it. 🤓
For now, here's a simple example:
```ts
let oneBigInteger: bigint = 1n;
```
Again, similar to the previous primitives, using `const` will result in a type literal:
```ts
const aBiggerBigInteger = 100n;
// hovering over aBiggerBigInteger shows a type literal of 100n
```
_**When in doubt, check it out! 👀**_
## 👾 symbol
Another primitive that I rarely use is a `symbol`. If you have a great example from your own experience, please share! 🤓
A `symbol` is an immutable and exclusive value for a property key created using the `Symbol()` constructor. Strings are optional to provide a key value used to access a `symbol` at a later time.
This is the only primitive with a reference identity making it unique. In some ways, they behave like objects. Often, a `symbol` can be used to add a unique property to an object that acts as a _hidden_ mechanism from other code that might typically access a key.
Each `symbol` creation is completely separate. Let's take a look:
```ts
let example1 = Symbol('example');
let example2 = Symbol('example');
console.log(example1 === example2);
// Output: false, symbols are unique
```
In addition, TypeScript has the concept of a `symbol` subtype - called `unique symbol` which allows a `symbol` to be treated as a unique literal from explicit type annotations.
Similar to the type literals in the above primitives, `const` can be used to declare a `unique symbol` or we can use a combination of `readonly` and `static` properties. To access or reference the `unique symbol`, the `typeof` operator should be used.
Let's look at an example that my friend GitHub Copilot helped generate using `const`:
```ts
// Define a unique symbol
const uniqueKey: unique symbol = Symbol('uniqueKey');
// Create an interface with a property of type unique symbol
interface MyObject {
[uniqueKey]: string;
}
// Create an object that implements the interface
const obj: MyObject = {
[uniqueKey]: 'This is a unique value',
};
// Access the unique property
console.log(obj[uniqueKey]); // Output: This is a unique value
```
_**When in doubt, check it out! 👀**_
## 🍽️ null and undefined
These primitives I use frequently! Both `null` and `undefined` express a lack of value, but there are subtle differences. To better explore them, I opted to describe `null` and `undefined` together.
**Let's start with `null`.** This means that a variable was defined and `null` explicitly assigned to express the absence of a value. This value may be intentional or to express that there is no known value yet to apply.
Here's a quick couple of examples:
```ts
// Example 1
let nullExample = null;
console.log(nullExample); // Output: null
// Example 2
const nameExample = db.findName();
// We make some call to a database to find a name object.
// No name object was found for the sake of our example!
console.log(nameExample); // Output: null
// If no name was found, we could return a null value
// to make this result clear without throwing an error
```
**Moving on to `undefined`.** This means a variable was declared, but not defined or a value was not assigned. The `undefined` assignment to the value happens automatically if you do not initialize that variable.
Let's take a look at a quick example:
```ts
let unassignedExample;
console.log(unassignedExample); // Output: undefined
```
**What are the more subtle differences then?**
First, `null` represents an intentional absence of a value, whereas `undefined` typically indicates an unintentional absence of a value. Of course, there are cases to use both intentionally, but `null` has to be assigned while `undefined` is automatically assigned when a value is not initiated.
That can be a little confusing, so let's take a look at an example of `undefined` where you might use it intentionally. Let's say we're trying to apply grade scores to a respective letter grade, but maybe there can be a glitch in the system providing the grade scores.
```ts
const numberGrade: number = NaN;
// Let's say we got this from an outside source
let letterGrade;
if (numberGrade >= 90) {
letterGrade = 'A';
} else if (numberGrade >= 80) {
letterGrade = 'B';
} else if (numberGrade >= 70) {
letterGrade = 'C';
} else if (numberGrade >= 60) {
letterGrade = 'D';
} else if (numberGrade >= 0) {
letterGrade = 'F';
}
console.log(letterGrade); // Output: undefined
```
Perhaps we can add some checks for such an `undefined` incident and handle it appropriately now.
The next difference is that `null` represents the absence of an object. In our earlier example, we expected a result to be an object containing details about a name record. On the other hand, `undefined` is a lack of _any_ value at all.
Finally, equality between the two differs. `null` and `undefined` are loosely equal (`null == undefined`), but not strictly equal (`null !== undefined`). Loose equality performs type coercion and both primitives evaluate to an absence of value. Strict equality, however, checks whether the data types and values are the same and `null` and `undefined` are ultimately not the same data types.
_**When in doubt, always check it out! 👀**_
**I hope that covering these primitives can be at least a little bit informative! In writing this I learned a few new things myself or at least considered aspects of these types I hadn't before. 🤓**
## 📚 resources for further reading
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
- ["TypeScript in 5 Minutes"](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html)
- [Basic Types](https://www.typescriptlang.org/docs/handbook/basic-types.html)
- [Wikipedia on TypeScript](https://en.wikipedia.org/wiki/TypeScript#:~:text=Development%20tools-,Compiler,that%20can%20execute%20the%20compiler.)
- O'Reilly Books:
- [Programming TypeScript by Boris Cherny](https://www.oreilly.com/library/view/programming-typescript/9781492037644/)
- [Effective TypeScript by Dan Vanderkam](https://www.oreilly.com/library/view/effective-typescript-2nd/9781098155056/)
- [Learning TypeScript by Josh Goldberg](https://www.oreilly.com/library/view/learning-typescript/9781098110321/)
- I haven't read this in full as of this writing, but I've seen him speak a few times, and I'm excited to start it after wrapping up a couple of other books!
- [TypeScript Cookbook by Stefan Baumgartner](https://www.oreilly.com/library/view/typescript-cookbook/9781098136642/)
- I haven't read it as of this writing, but I've heard good things and it's on my list!
## capturing curiosity
URL: https://mindiweik.com/blog/capturing-curiosity/
Published: 2024-11-19
Twice this fall, I shared something I have been dreaming about and working on personally. I’ve struggled to recapture curiosity lately, but that doesn’t mean it’s gone!
I took an ambiguous career path ([read more about that here](/blog/the-power-of-career-change)). For the first time a few years ago, I followed my personal curiosity, which morphed into my tech career! I’ve been rebuilding my “curiosity muscles” using tactics identified through my own research that can be practiced over time.
Let’s be clear: I am not an expert on curiosity or perfect at practicing it. However, I am genuinely interested in building up and practicing curiosity. I've been seeking this type of “exercise” for a long while, and I am currently practicing my own techniques. I am SO happy to share them with you, too!
**Here, we’ll cover:**
1. 🎬 Video + Presentation Insights
2. 🤔 What is Curiosity?
3. ⚖️ Benefits + Challenges
4. 👉 The 4 Core Concepts
## 🎬 video + presentation insights
Below is a recording of the first presentation for “Capturing Curiosity”, a collaboration with Coding with Callie in September 2024. It was a blast to finally share this content! The actual presentation is about 40 minutes long, followed by a Q&A.
If you have any feedback you would like to share, I’m open to hearing it! [Link to the current slides, if interested!](https://www.canva.com/design/DAGRKyHC3TE/FUsGdL0j9XI1Ck96kVq2eQ/edit?utm_content=DAGRKyHC3TE&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton)
**Lessons learned in the first presentation:**
- Consider the timing for outside lighting. 😅 I didn’t realize until halfway through that I was partially in shadow. I don’t usually record that time of evening!
- The Q&A highlighted a few lacking areas of my initial introduction story, so I added more detail for later iterations. ✅
A few weeks later, I shared only my intro in another online setting, my Toastmasters group, where I’m excited to learn and grow into a more professional presenter. Though it was a brief glimpse into the talk, I received helpful feedback on my physical and verbal presentation techniques rather than the content.
About a month after the first full talk, I shared this again at the [PLoP 2024](https://plopcon.org/plop2024/) conference, focusing on “Pattern Languages of Programs, People & Practices.” Applying curiosity fits each of these because you need to consider how you will interact with people, how you can apply your curiosity to software - or other tangible things - that you build, and how you approach practices in your day-to-day, personally and professionally.
This third event was also live but not recorded. In this space, we focused more on the patterns and a pattern language concept as it applied to curiosity.
**I received advice about the structure of the content along with other learnings:**
- The previous group ended late, so we started late. I cut a fair bit of the original talk in real time. It would benefit me to identify before a talk where I could best cut to still provide a great experience to the audience.
- Questions or discussions happen unexpectedly in a more collaborative, in-person, and live environment! More than once, my audience interrupted me with excellent insights, suggestions, and questions. This was valuable to all! Again, identifying options to cut in advance would have helped me cultivate this live experience without losing too much of the core.
- There was confusion in **Ask the “Right” Questions** that could have been more clearly outlined. Using the “anti-pattern” term would help better identify the negative behaviors. (I have not made this change, but will use this for a later iteration!)
- Upon reflection, some of the examples I use feel immature. My audiences are intelligent adults. I want to add stronger examples to practice with. I cut a robust set of examples in the first and second iterations because I was limited on time, but if this is ever used for another collaborative or workshop-style presentation, I’d love better options!
I learned so much through these experiences! I appreciate everyone who spoke up to ask questions and who provided me feedback individually, including friends, family, and LinkedIn connections who watched the recording afterward. 🙏
## 🤔 what is curiosity?
Selfishly, I research curiosity to better understand it and continue to learn and grow as a human being. I’ve worked to reprogram my brain and retrain my actions to lean into my own curiosity. I’ve tried to simplify and distill what I found to make it easy to digest and, therefore, put into practice as a pattern of behavior!
> **Curiosity is so much more than a simple desire to know more about a topic, a concept, or a question. It’s a fundamental aspect of our cognition and the human experience.**
Think about it: As young children, we are immediately and naturally curious about our surroundings. Everything is new, and we learn daily about the people around us, the “rules” of our society and culture, and the world itself.
In reading about both curiosity and boredom, there are essentially 3 zones:
- **Understimulation**
- Think of menial tasks or situations that lack sufficient novelty or complexity.
- **Overstimulation**
- Consider feeling conflict, fear, overwhelm, or uncertainty.
- **“Just right,” or “sweet spot,” or “Goldilocks zone”**
- When you’re in an engaged state without feeling pressured somehow.
We seek a sweet spot between under- and overstimulation. Although we seek this, our curiosity peaks most when under- or overstimulated. Often we are either bored, looking for something to excite us, or uncertain and wanting to know more about our situation or surroundings to find comfort.
Curiosity is an impulse to explore, a spark for novelty, and a drive to discover without necessarily having an end goal. A thirst for knowledge guides our unique ways of interacting with the world through a mindset and a pattern of individual behaviors.
## ⚖️ benefits + challenges
The **benefits** of our curiosity are plentiful and relatively simple to understand. Here are a few we can review:
- Build stronger mental pathways to form stronger memories
- Build more meaningful connections with others
- Build trust with others
- Expand your empathy, getting to know the experience of others
- Breed more creativity and a variety of possible solutions in the face of a problem
- People tend to think curious leaders are _more_ _likable_ and _more competent_
That said, there are 3 major **challenges** I’ve identified that can be potential blockers. We should be mindful of these, which can lead us to avoid practicing curiosity.
### **discipline**
This one is the hardest for me. When I think of curiosity, I think about excitement, spontaneity, and the ability to embrace change. That’s a part of it, but it’s not the whole picture.
> Like many things in life, it takes dedicated practice to become “good” at something.
If you are consistently complacent in that “sweet spot,” you may lose touch with your curiosity. One must tread a delicate balancing act to reach under- and overstimulation between moments of comfort. For some, this may come more naturally.
Different people will find practicing each of the four concepts below easier or harder than others - it just depends! In any case, it takes practice to build a habit that will become more automatic.
### **fear**
Another challenge most of us face is fear. It comes in many forms, but with curiosity, it seems to lean more toward a fear of judgment from others. Curiosity takes a lot of guts in some instances!
For ages, we have naturally conformed to the expectations of our tribes because it has meant survival, safety, and community.
> You may need to question the status quo. It can be uncomfortable.
Often, companies and teams express a value for the curious trait. But almost as often, leaders fail to relinquish their fears that embracing it will lead to less efficiency and higher costs. It’s true that, in some sense, embracing curiosity is a risk; we don’t know what the outcome will be. But ultimately, this valued trait is an _expression_ of value only because of the fear of failure.
Please remember that you don’t have to necessarily act on anything, especially if you are truly uncomfortable. Even asking questions to challenge something small, like, “I wonder how greeting cards became a staple for birthdays and holidays,” could lead to an interesting exercise and possibly bring some new insights.
### **humility**
It’s also extremely important to apply your curiosity inward. You can learn so much about yourself if you utilize the tools toward yourself as a human being, but it’s tough to ask the really hard questions.
The best approach to removing yourself and your emotions from the scenario is to pretend you were a third-party observer. Consider the words that were said or the actions that were taken to see if you might glean something new.
> It’s okay to admit what you don’t know and lean on experts when you need them!
Don’t be afraid to dig a little deeper than you are comfortable with, but also be willing to be gentle with yourself. You can struggle and recognize that struggle without being too hard on yourself. If you can take what you learn and apply those lessons moving forward, it’s worth the extra push. You’re just like everyone else; you’re still learning!
The more you build your curiosity, the more you will open yourself to other points of view. Humility will allow you to listen better, hear shared experiences, and more empathetically consider others.
## 👉 the 4 core concepts
My goal is for you to take away actionable patterns and tools. I’ve distilled much of what I found to keep the message clear. This list is small, but these core concepts can make a big impact!
### **#1 observe & investigate**
To start off easy, you can just sit back, relax, and observe. I’m serious!
Easier said than done for some; I like to stay on the move, and I have difficulty slowing down to take in the full view. If you’re like me, I recommend finding a method that will help you take a step back.
Ideas:
- meditation
- music
- a warm mug of tea or coffee
Or it could be something else entirely. I love to cook. When cooking, I am calm, and I can think through things, consider a problem I am facing, or spend time on something I’ve wanted to ponder all day.
When you reach this state, you can observe various aspects of the world around you!
Observation is more than just a passive tactic, however. Harness it to become an investigator! With the rise of AI, deepfakes, and fake news, along with social media driving comparisons, you should be willing to review and question everything, even things that are “well-known” in society.
Seek out unbiased news sources and practice discerning valid options. You can read articles, books, forums, social media, blogs, and newsletters or consume videos, courses, and podcasts.
Remember that you can also ask fellow investigators! Understanding beliefs and opinions from varied perspectives is helpful, keeping in mind that they are not sources of fact but rather of experiences.
All of that said, there are a lot of words out there. It’s a good idea to avoid information overload. Once you find some sources you feel you can trust, spend more time considering the subjects. Review your sources occasionally to confirm continued validity. I recently heard a great way to think about this: Treat your information intake like nutrition. It should be varied and diverse, the sources should be nutritious, and you should keep portion control top of mind.
All that is to say, be selective.
### **#2 embrace failure**
This tactic is often difficult to practice for most. Embracing failure is hard when we’re drilled over and over to avoid failure, whether pressured to get good marks in our education or work extra hard to get a promotion.
The good news is that you don’t have to jump straight into failing, especially when the stakes are high. Start very small and learn to embrace failure where you are.
To practice this, you can try something fairly innocuous, like a board game or maybe a small challenge when learning a new language (coding or spoken language)! The consequences of failing are rather small.
Take a moment to assess how you feel afterward. Ask yourself what you can learn from this particular experience.
For instance, during COVID, I picked up crochet. Of course, I want the end product to look good because I’m putting in time and effort, but on more than one occasion, my work was “funky.” I admit I was a tad disappointed.
What did I learn from these experiences?
1. I may need to use a tool to keep track of my stitches. I tried to make a difficult shirt and kept losing count, resulting in an unfinished project due to the “funky” mismatches part-way through.
2. When I was working on a small stuffed octopus, it took a few attempts to figure out how to position my needles to get the right angle for a rounded effect. I made a lot of these creatures.
3. Similarly, when working on the octopuses, I learned that my directions about where to place the legs were unclear. There were many “squids” with 6 legs instead of “octopuses” with 8 legs.

It took me several attempts to get the octopus right. Each time, though, I learned something new and gained a new approach. Even in some of those failures, I learned some options and ideas I could use in other projects where it had an effect I _did_ want!
The point is that even when you encounter failure, it may or may not help you in the future. However, you’re expanding your toolbox for the next problem you encounter, the next project you work on, or the next idea you want to approach.
### **#3 ask the “right” questions**
There is true value in the ability to question knowledge. Even people we typically consider smart - like scientists - are often wrong and must iterate on what we “know” as a human species. This also applied to explorers! Consider Christopher Columbus, whose claim to fame you may know as an explorer who “discovered” the Americas. He thought he was in the East Indies!
As we venture forth, the more questions we ask, the more we grow our understanding of ourselves and others. Ultimately, as humans, we _want_ to be understood and to understand our surroundings.
**Pattern approaches you can use to ask improved questions:**
1. **Check your intention:** Pause and ask yourself, “What is my intention?” Often, we judge others based on their actions because we can’t know their internal intentions. With ourselves, we can learn our intentions. This takes the ability to be honest with yourself, but hopefully, if we know our intent, we can ask more pointed questions or rephrase a question.
2. **Why?:** Be careful with this one-word question. Many are triggered by a why because it feels like you are questioning them, their authority, or their approach. Honestly, the responder probably doesn’t know the answer! It can cause negative reactions, and likely, you will not find the information you seek, nor will it align with your intention. There are ways to rephrase your why to reach the desired result and still reach the “why” behind.
- Instead of “Why are we doing X?” consider something like, “What will improve if we do X?” or “Who may benefit when we finish doing X?”
- Instead of “Why did you choose to do Y?” consider something like, “What helped you make the final decision to choose Y?” or “How did you approach Y to figure this out?”
- Instead of “Why am I going to work with the Z team?” consider something like, “How did you decide on team dynamics?” or “Which skills do you think I can bring to best help out the Z team?”
3. **Find a spotter:** Ask a friend for help! Find a spotter to help monitor your questions and check in to see if the phrasing, attitude, etc., aligned with your intention. This could be a coworker, a partner, a friend, or children/grandchildren. Kids can be brutally honest! Just make sure it’s someone you trust who can be honest with you so that you can learn and improve over time.
4. **Pause:** Pause to your advantage! This is challenging, but ultimately, it gives the responder time to _think_, and you will be less likely to “lead” them to answers they wouldn’t have chosen otherwise. This encourages the responder to go further in their answer and motivates you to truly listen to their unfiltered answer.
5. **Disguised questions:** Similarly, avoid statements or feelings in the form of a question. For instance, “Do you think that [so-and-so] is taking advantage of the company when they take long lunches?” These leading questions won’t help you find real answers; your biases will taint them. Rapid follow-up questions also cause this. Even if you start open-ended (see next point), fast follow-ups can alter the response. If our intention is truly to learn, then we should consider our phrasing to refrain from those leading questions that are likely inaccurate.
6. **Open-ended questions:** You may have heard about open-ended and closed questions. A closed question usually leads to a “yes” or “no” or a direct answer like, “What’s your favorite color?” “Green.” Open-ended questions seek deeper answers like, “How did you celebrate your birthday last week?” To learn more about someone or a subject, we should lean toward open-ended questions to dive deeper. Closed questions serve a purpose but allow the responder to decide how much information to provide. You hand over the power, which may squander your chance to practice your curiosity muscle if have limited opportunity or time!
Finally, let’s combine a few of these things and discuss the “questioning funnel.”
**There are 4 keys to the questioning funnel:**
- open-ended questions
- questions to probe, clarify, and reflect
- the questioner summarizes and confirms what they heard
- ideally, the questioner reaches the target and broadens their understanding
The questioning funnel
open-ended
↓
probe, clarify, reflect
↓
summarize, confirm
↓
🎯 target
If you’d like to see/hear an example, in the recording, I discuss a scenario where someone might ask about an ash tree cut down in my backyard earlier this year.
**Here’s the simplified version:**
- **Start at the bottom:** Hear me out. You need to identify your target to better frame your questions! This is also a great moment to check in with yourself to discover your _intention_ with this conversation.
- **Top of the funnel:** Now, let’s go through the process. To start, you want to ask those open-ended questions. Ideally, the responder will share information to guide your follow-up questions.
- **Begin to narrow:** The answers provided may help you ask more probing or clarifying questions. If needed, ask additional questions about the received information to get closer to the target.
- **Keep narrowing:** You may need to ask closed questions to improve your understanding. Then, you should be ready to summarize and paraphrase what you heard to confirm a shared understanding.
- **Repeat as needed:** If you misunderstood something or the information gives you a different perspective, you may want to revert to open-ended questions and go through the process again.
- **Reach the target:** Ideally, you will reach the target in your conversations! This means you have an answer to the question you started with and a shared understanding.
Over time, you can also try applying these questions inward. Take time to learn from your mistakes, however big or small, and ask yourself these “better” questions.
> “Being **curious** can manifest itself in the activity of asking questions, but it can also be a position from which one approaches their life.”
>
> - From [gostrengths.com](https://gostrengths.com/what-is-curiosity/)
### **#4 seek new experiences**
When we were very small, almost every experience was new. We were in exploration mode! These many “firsts” slowly diminish as we age. Don’t fret; there are still plenty of opportunities!
If there is something you’ve always wanted to see if you like it, try it! This might be:
- Classes or activities
- Travel
- New media (like VR/AR/AI)
- Join a community
- Attend an event
This can be easier said than done. Not everyone is ready to jump into a big adventure from the start. Start with something minor in your daily routine, like walking your dog on a new path, trying a new recipe or food, or trying out a new style. Even small changes can make big impacts!
Each experience is an opportunity to open your mind to new perspectives, provide varied experiences to increase your creativity and expand your empathy for others.
For example, let’s consider the new path when walking the dog.
Perhaps you’ll go through a new-to-you neighborhood and meet a friendly new face. Maybe you see a community garden, and you’ve wanted to get involved in something like this. Now you have a lead! Or what if you run into construction or an unsafe environment and must overcome a challenge to navigate this?
In any of these cases, you’ll learn something new!
## conclusion
Now armed with a pattern framework, try to do a few repetitions each day.
Building up curiosity as a skill takes practice! Just like with meditation, you likely won’t be able to guide the flow of thoughts and sit relatively still when you first start. As you practice, bit by bit, you begin to have a little more control over your thoughts and more capability to take stock of your body as you practice.
**If you’d like to build curiosity through a practice similar to meditation, check out the “Reverse Meditation” concept toward the end of the talk recording!**
I thought it was the opposite of meditation, so I called it this. However, the PLoP conference attendees helped me realize it’s similar to guided meditation! While practicing “reverse meditation,” I recommend trying to practice the concepts:
- **Observe & Investigate:** Consider your “environment” or how that may affect you.
- **Embrace Failure:** You may not get it perfect the first time and that is okay!
- **Ask the “Right” Questions:** Ask yourself well-formed questions to think outside the box and act as a third-party observer as much as possible.
- **Seek New Experiences:** Consider how this practice could be different, or think about other things you may want to try during your “meditation.”
The best part about this type of exercise is that it’s FREE! Set aside a few minutes whenever you need it!
Everything we do has the potential to stimulate a curious mind, even in unexpected moments. You now have a pattern toolkit to help you branch outside the box when needed! Increasing the frequency of your curiosity practice should help you start to see new perspectives and consider new ideas, allowing you to think of solutions you may not have otherwise.
Use your discipline to maintain your pattern practice once you feel you’ve got a handle on curiosity. Do your best not to let the fear of what others may think leak into your practice. You’ve got a lot to bring to the table by embracing failure and asking questions where they are warranted. Consider the impact you could make!
There have been many small moments when I realized that, in asking these questions, even internally, I am shocked to find that there is really no good reason or that actions and thoughts are based on old or inaccurate assumptions! Challenge everything, investigate, and review your resources.
Give it a go and start to ask yourself the really hard questions. Welcome the humility that may arise when you do. You will surely learn more about yourself and perhaps gain insight into how others hear you speak and perceive you.
With these pattern tools, we can start having fun with more things we do. I’m still a work in progress. I have found that I genuinely enjoy life more when I can take that step back to observe and make the time to find and enjoy new experiences, however big or small.
**I’m hopeful you can take away at least a small spark of inspiration to spread that joy I felt when I found technology and decided to bring my curiosity into my career… wherever you need it most. ✨**
## dependencies or dev dependencies, that is the question
URL: https://mindiweik.com/blog/dependencies-or-dev-dependencies-that-is-the-question/
Published: 2024-11-14
I, a human coder, made a silly mistake. There was a time when I moved a package in our project’s package.json file from the dependencies section to the devDependencies section because it made sense to me then. How wrong I was!
Thankfully, it didn’t have a huge impact; it was for a developer tool and a new-to-us package. The issue resulting from the change was caught quickly. We rectified it with ease, and we all lived happily ever after.
However, I know I’m not the only one to fall into this trap. I thought it would be good to caution my former self and others who may not yet know the differences between a dependency and a dev dependency.
**Here, we’ll cover:**
1. 📂 Package & Module Definitions
2. 💎 What is a Dependency?
3. 🧑🏾💻 What is a Dev Dependency?
**I’ll share some examples and my story. We’ll keep it simple.** 😉
## 📂 package & module definitions
Let’s quickly define a “package” and a “module” in software development to ensure a common understanding.
A **package** is a file, directory, or resource set that can be publicly or privately shared. These packages can provide useful functions or tools for your project and are defined within a package.json file.
That package.json file is important! It holds all the details about your project, like authors, licensing, and scripts. It also houses your list of dependencies and devDependencies and the specific version numbers your project needs to work as intended.
One of the most common software registries for packages is **[npm](https://docs.npmjs.com/about-npm)**. I’m sure you’ve interacted with it at some point! My teams work a lot in Node, and npm also helps share and use **Node modules** similarly, loading something directly _from_ a JavaScript or package.json file for use in your project.
In any case, npm uses your package.json file to determine which packages or modules you will need for your project. Remember that you will list a version number that npm will use to install the corresponding package. Take an extra moment to consider this for your project; failing to do so could result in feature compatibility issues, bugs, and instability.
Packages allow individuals, organizations, and companies to take advantage of existing solutions, saving time and resources. They also typically provide documentation to help users apply these solutions to their project needs. Keep in mind that the quality of documentation can vary greatly!
But using packages and modules is not all 🌈 rainbows and unicorns 🦄.
When you add tooling like this, it needs ongoing maintenance - someone has to update it, fix bugs, and improve the documentation.
Hidden security risks can also exist, especially if the creator(s) no longer maintain it.
## 💎 what is a dependency?
Dependencies are ultimately any package used in your project. Dependencies should be added to your package.json file's dependencies object to help your project function properly when deployed.
You might use these packages for development and testing, or they might be tools, libraries, frameworks, or other packages that improve an end user’s experience.
The key thing to note is that these packages should be **needed to run in production**.
Here are a few packages we regularly use for Node API projects:
```js
"dependencies": {
"express": "^4.21.1",
"pino": "^9.5.0",
"zod": "^3.23.8"
}
```
Consider these packages and why they ended up in the dependencies list.
If you’re unfamiliar with these packages, here’s the gist: [Express](https://expressjs.com/) is a Node framework commonly used to create a project server. [Pino](https://getpino.io/) is a fast and lightweight logging library. [Zod](https://zod.dev/) is a schema validation library we use to ensure that our “outside data” conforms to the TypeScript types we utilize in our codebase.
Now let’s take a closer look at _**why**_ each of these packages belongs in the dependencies section of the project.
Express is a bit obvious. How would an end user send over an API request without a server to connect with? We need a server on the production deployment for our users to reach our API. Easy enough!
What about Pino? As a logging library, we use this to send logs to our deployment cluster for local logs and to the observability platform to view all of our logs in one place. Yes, we need and use logs during development to ensure things are working as expected or failing as expected in some cases. However, arguably more importantly, we need our logs in our production deployment to tell what’s going wrong and why in the case of unexpected issues.
And Zod? Especially because we use TypeScript in my team projects, a more strictly typed language than vanilla JavaScript, we need to ensure the data we receive “from the outside world” is what we expect. Regardless of language, a validation library lets us immediately stop invalid types in their tracks, keeping our app safer! As you might imagine from my earlier quote, “from the outside world,” we expect these schema validations to be used in the production deployment. How else would we check that users are sending us what we expect?
Hopefully these examples provide a bit more context. To reiterate, when we need to use a package in the production deployment or if a tool is needed to interact with or validate user input, this is usually an indicator that your package needs to be located in the dependencies list.
## 🧑🏾💻 what is a dev dependency?
Now that we’ve established what a dependency is, let’s look at the other side of the equation - devDependencies. The devDependencies packages are specifically used for **local development and testing**.
In other words, if there is a package or module you only need to use during development, and your app doesn’t need it when deployed into the wild, that package should be added under the devDependencies object in your package.json file.
Here are a few packages we regularly use for Node API projects, along with the “oops” package, which we’ll talk about toward the end:
```js
"devDependencies": {
"@bugsnag/js": "^8.1.2", // my offending "oops"
"c8": "^10.1.2",
"eslint": "^9.14.0",
"typescript": "^5.6.3"
}
```
**Let’s start with the packages that belong here.**
If you’re unfamiliar with these packages, here is the gist: [C8](https://github.com/bcoe/c8) is a tool we use to check our test coverage natively in Node. [Eslint](https://eslint.org/) is a popular linting utility tool to ensure your project follows the standards you put into place in your config file. This will typically follow your team or organization’s determined linting rules. [TypeScript](https://www.typescriptlang.org/), which I’ve already mentioned, is a language that happens to be a superset (or added features on top of) of JavaScript.
C8 is purely for our internal purposes. Tests are frequently run locally, in development environments (where the app is actively being worked on), in staging deployments (which simulate the production environment), and in CI/CD pipelines. Tests in a production deployment serve no purpose and could negatively impact performance, especially as a project scales. Ultimately, C8 is for dev eyes only! 👀
Similarly, eslint is not something our users should know about. They won’t be looking at our codebase, and it’s unlikely that users would know enough about code - or our particular codebase or domain - to nitpick on our cohesive linting. Unless you’re working on an open-source project. But that’s a different story.
What about TypeScript? One could argue that you would need _some_ level of language available to actually run the code, right? [TypeScript doesn’t exist at runtime](/blog/exploring-typescript-runtime)! Runtime code is essentially the executed code your users interact with. JavaScript files are created in the project's “build” or compile step, and Node can execute these files, so TypeScript is unnecessary in the dependencies section. Where TypeScript shines is its ability to write type-safe JavaScript code _before_ it is [compiled](/blog/exploring-typescript-ts-compiler).
**Let’s talk about the 'oops' moment when I mistakenly moved a package that should’ve been in `dependencies` to `devDependencies`.**
My error came when I moved two [BugSnag](https://www.bugsnag.com/) packages, like “@bugsnag/js” listed above, to the devDependencies section. We implemented BugSnag into a new project as a tool to monitor errors and track them down to squash them quickly! At first, it seemed like BugSnag should only be needed in devDependencies because it was a _**developer tool**_. Our users would not be interacting with it.
You may already see why that is an issue, but this made perfect sense in my initial mental model. When this change was deployed into our production code, we noticed an issue immediately.
**We used BugSnag references in our handlers and application-level code that relied on this package and our production deployment couldn’t compile and build!**
I’m grateful that the issue was quickly noticed and easy to fix. The team immediately reverted this to the dependencies section, and all was well. We implemented BugSnag to monitor errors and track them in real-time in the production environment, which is why it needed to be listed in dependencies. Without it in production, we couldn't run the app, nor reach the goal to catch and fix issues promptly.
Although dev tools are _often_ only needed in the devDependencies section, consider whether it is actually used in production. In our case, BugSnag needed to be available in production because how else would we find and catch those bugs?
Of course, that makes sense. They say hindsight is 20/20.
**I hope this cautionary tale is helpful for those who have just learned about** dependencies **and** devDependencies **or a good reminder for those who already know. Take an extra moment to think about your packages and where they belong!**
## 🤓 continued reading:
If you want more details, I used these sources to help me refine my thoughts and phrasing!
- [npm - About Packages and Modules](https://docs.npmjs.com/about-packages-and-modules)
- [npm - Specifying dependencies and devDependencies in a package.json file](https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file)
- [DhiWise - Mastering Package Management: DevDependencies vs. Dependencies](https://www.dhiwise.com/post/package-management-devdependencies-vs-dependencies)
- A great, in-depth article. **BONUS**: They also discuss peerDependencies!
- [Stack Overflow - An old, but incredibly helpful question and answer](https://stackoverflow.com/questions/18875674/whats-the-difference-between-dependencies-devdependencies-and-peerdependencie#:~:text=Summary%20of%20important%20behavior%20differences%3A)
- **BONUS**: peerDependencies mentioned here
- [Geeks for Geeks - Difference between dependencies, devDependencies and peerDependencies](https://www.geeksforgeeks.org/difference-between-dependencies-devdependencies-and-peerdependencies/)
- **BONUS**: peerDependencies mentioned + a useful table!
## the software engineer's guidebook review
URL: https://mindiweik.com/blog/the-software-engineers-guidebook-by-gergely-orosz/
Published: 2024-10-08
Similar to an [earlier post on Clean Code](/blog/clean-code-by-robert-c-martin), this is another book overview! The [Software Engineer’s Guidebook](https://www.engguidebook.com/) (shortened to SWEG below) is written by Gergely Orosz, author of [The Pragmatic Engineer](https://open.substack.com/pub/pragmaticengineer) newsletter. Curious about his process? He [shared his experience](https://newsletter.pragmaticengineer.com/p/software-engineers-guidebook?r=29u7hv&utm_campaign=post&utm_medium=web) authoring the book!
With my education budget, I purchased a copy of SWEG! I’ve considered whether to steer my career into leadership or a more technical track. This book is well worth it, covering multiple aspects of both options. I’ll undoubtedly reference this for many years!
Below, we’ll review the book's first two sections. They cover career fundamentals and building competence, which I chose to focus on because they are the most actionable sections. I also decided to present this part to my team.
_**Although I cover a lot from the book, I’ve added my own interpretations and content simplifications. What you see here is only brushing the surface.**_
_**If any of this interests you, please [purchase a copy](https://www.amazon.com/Software-Engineers-Guidebook-Navigating-positions/dp/908338182X) for yourself! 👌**_
**In this overview, we’ll cover:**
1. 🎬 The Presentation
2. 💡 My 3 Big Takeaways
3. 🔨 Career Fundamentals
4. 🔧 Building Competence
## 🎬 the presentation
I [recorded a Loom presentation](https://www.loom.com/share/5ac9c21df8524559b6f8b824d6d35f7b?sid=8515e32e-f773-4d1a-b457-4d8f0d6ac798) if you’d prefer to hear and/or visually see this information. It’s about 22 minutes long. [Here is a link to the slides!](https://www.canva.com/design/DAGKHoLXYFo/88WU5ztzzdR4B70ygO3VHQ/edit?utm_content=DAGKHoLXYFo&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton)
## 💡 my 3 big takeaways
1. No one will advocate for your career better than you. Take ownership!
2. Write it down. Yes, everything!
3. Each journey is entirely unique.
### 🫵 no one will advocate for your career better than you. take ownership!
When stated, it seems obvious. You are in control of your own career. In my [Power of Career Change](/blog/the-power-of-career-change) post, I talked about “ambiguous ambition,” where I didn’t have direction in my career path. _**I am happier actively making goals, completing activities toward those goals, and fulfilling a career trajectory.**_
I bring related topics to my one-on-one meetings. _Here’s [a terrific read](https://read.highgrowthengineer.com/p/you-and-your-manager-3-ways-to-work?utm_source=publication-search) in a collaboration post from [High Growth Engineer](https://open.substack.com/pub/highgrowthengineer) on working with your manager(s)._
### 📝 write it down. yes, everything!
Documenting accomplishments, challenges, and projects cannot be understated. _**Don’t forget to add peer feedback!**_ You will better understand what you’re doing now and what’s next. This is also a solid tool when discussing your role. This could be a promotion conversation aid, a resume update reference, or proof if you are in a sticky situation.
Be consistent and store it on a personal device or account to retain access. I have a weekly reminder, tried a few templates, and currently use a [Trello board](https://trello.com/templates/productivity/self-advocacy-record-A7mAFfdr) adaptation. (Other options: [Brag Document](https://jvns.ca/blog/brag-documents/), Notion, spreadsheet(s), etc.)
### 🦋 each journey is entirely unique.
Through Meetup, I meet women from all over who reside in my state and work in tech. Their stories and tech roles are different, even if they have the same title! The best we can do is learn from other’s experiences and apply their lessons to our challenges.
_**Whatever advice, suggestions, and feedback you receive, you are responsible for discovering how and whether to apply it.**_ Gergely did an excellent job clarifying that in this book. The information is a reference, not explicit instructions.
## 🔨 career fundamentals
The book starts with core aspects of career growth and ideas for reflection. We’ll touch on some concepts, but I encourage you to read it to understand them fully!
> "Career paths are diverse, and there’s no simple way to define what a “good” career path looks like, as this varies from person to person. The best you can do is figure out which career path is interesting and achievable for you."
_→ I won’t touch on compensation, company tiers, performance reviews, promotions, and switching jobs here, but the book covers these in detail._
### 🏢 types of tech companies
There are several types of companies that hire technical workers.
- **Big Tech** - Large, publicly traded companies that are commonly known (think MAANG; Meta, Amazon, Apple, Netflix, Google, or other variations), with market caps in the billions of dollars. These companies impact hundreds of millions of customers and hire tens of thousands of Software Engineers.
- **Medium to Large** - Tech-first companies with Software Engineering at the heart of their business, like Atlassian, Dropbox, or Shopify, employ hundreds or thousands of engineers. Though they still offer wide-impact opportunities, their customer base tends to be smaller than Big Tech’s.
- **Scaleups** - Venture-funded companies are often later-stage startups with product-market fit investing in growth. They may intentionally take a loss or move fast under high pressure. Some examples are Airtable, Klarna, and Notion.
- **Startups** - Although venture-funded, startups depend on, and may secure, smaller funding rounds and seek product-market fit. They can be risky, less stable, and have poor work-life balance. However, engineers have freedom with work variety and growth due to a smaller headcount. An example is Airbnb (before graduating to a Scaleup).
- **Traditional** - Often non-tech companies with tech divisions supporting the core business. Think JP Morgan, Toyota, or Walmart. Tech may be considered costly with fewer staff+ paths, but it provides solid work-life balance and stability. Some keep tech front and center. Maybe they started as standouts and are now mature, reliable, and profitable with rigid organizational structures. They typically offer customer impact closer to Big Tech with more stability, work-life balance, and tenure. Examples are Broadcom, Cisco, Intel, and Nokia.
- **Other** - Examples are non-venture-funded (bootstrapped) companies, the public sector or government, nonprofits, consultancies/outsourcing/developer agencies, and academia or research labs.
With such variety, how do you know what’s right for you? That may take some thought. Narrow realistic options based on your circumstances. Talk with other engineers to learn about their experiences and ask questions!
### 👟 types of career paths
Once you find a role, how do you determine a path? Each is unique and could change over time, but how do you get there?
**☝️ The Single-Track Career Path -** In some cases, engineers move from an IC (Individual Contributor) role to management. They may remain long-term or later return to engineering if unsatisfied, possibly moving companies in the process.
| Level | IC | Manager |
| ------- | ------------------- | ----------------- |
| Level 1 | SWE | |
| Level 2 | Senior Engineer | |
| Level 3 | Staff/Principal Eng | Manager |
| Level 4 | | Director |
| Level 5 | | VP of Engineering |
| Level 6 | | CTO |
**✌️ The Dual-Track Career Path -** This is the flexible path; it’s probably more applicable to most because of that flexibility!
Some engineers stick with IC, some follow the single-track path, and some switch between them.
| Level | IC | Manager |
| ------- | ----------------- | ----------------- |
| Level 1 | SWE | |
| Level 2 | Senior Engineer | |
| Level 3 | Staff Engineer | Manager |
| Level 4 | Senior Staff Eng | Director |
| Level 5 | Principal Eng | Senior Director |
| Level 6 | Distinguished Eng | VP of Engineering |
| Level 7 | Fellow | Senior VP of Eng |
| Level 8 | | CTO |
### ⚖️ cost centers and profit centers
Some teams or projects are considered cost centers, while others are profit centers. Each has valuable experience, and working in both provides a more well-rounded engineering perspective.
#### **📉 cost centers**
Cost centers are needed for smooth operation, but they don’t typically generate business income. The challenges tend to be more interesting. Experienced engineers may join a cost center to build creative solutions for more significant problems. A downside is turnover. These teams are often targets when businesses need to reduce costs or engineers seeking career advancement could transfer to a profit center team.
#### **💰 profit centers**
Put simply, profit centers directly generate revenue for the business. Promotions can be more achievable in a profit center; it’s usually easier to reward profit! Performance reviews usually get better “scores” and bonuses, too. Engineers may join a profit center team to focus on growth or more straightforward challenges.
### ✨ career progress alternatives
There’s more to a career than finances and benefits, right? Absolutely! Here are a few to consider in your long-term career.
- **Culture -** Consider if you align with the mission and values of your company. Do you appreciate any societal contribution they provide?
- **Flexibility -** Do you have set or flexible hours? Is your role in-office, remote first, or hybrid? There may also be an on-call rotation to consider.
- **Personal -** You may prefer to “leave it at work.” You may have your own reasons for (dis)liking a company or a team. These are perfectly valid.
- **Health -** Consider actual or possible effects on your physical or mental health.
- **Growth -** Are there opportunities for growth or options to progress your skills and career? You don’t want to stagnate!
- **People -** This includes your relationship with your manager and peer dynamics.
### 🎯 owning your career
No one else will advocate as well as you can throughout your career.
#### **🏹 you’re in charge**
It’s up to you! Set goals, track goal efforts, and iterate. Managers don’t usually have enough bandwidth to grow individual careers. Here are some ways to take charge:
1. Tell your manager(s) and peers what you care about.
2. Share work you did that others may not notice.
3. Create opportunities for feedback from others.
#### **🏆 be seen as someone who “gets things done”**
The easiest way is actually to get stuff done! Ideally, your work is high-quality and completed reliably. Finish the work you commit to and overdeliver when you can.
Spend your efforts on impactful projects. How do you find these? Explore and ask questions to understand team and business priorities. Focus on these essential tasks.
Talk about things you complete! Don’t go overboard, but your manager should definitely hear about it. Share it if there is a measurable business impact, the work was unusually complex, or it took great effort.
#### **✏️ keep a work log**
Identify and stick to a solution and a cadence that works for you. What should you track? Significant code changes, code reviews, design docs, discussions, planning, helping others, postmortems, and anything else that takes time and has an impact.
A work log is powerful for identifying priorities and keeping them top of mind. You may feel empowered to say “no” or re-prioritize. Viewing completed work can bring a sense of accomplishment, too. It’s an incredible reference to quantify your impact.
#### **💬 feedback**
Give and receive feedback as often as possible!
So, if someone shares constructive feedback with you, remember it would’ve been easier for them to say nothing. Keep this in mind if your instinct is to react defensively.
**Receiving:**
- Hopefully, others will be respectful. Even if they’re not, there are useful nuggets.
- When you receive unclear or unsolicited feedback, ask for specific examples or clarify the impact to help uncover the useful nuggets.
- At times, you may need to seek out feedback actively.
- Regardless of whether you asked for it, you decide if you enact the advice.
**Giving:**
It’s helpful to consider your approach for positive and critical feedback.
- **Positive:** Call out good work when you really mean it. Share specifics about what you liked and why.
- **Critical:** Make it clear that you want to help and try to end positively. Focus on situational observations and impacts, clarify that it’s only an observation, and avoid telling them what to do. Deliver a critique in person (or via video) to reduce the chances of miscommunication.
#### **🤝 ally with your manager**
Your manager will impact your career. It’s a significant workplace relationship! One who believes in, advocates for, and supports your career goals can make a huge difference. How can you foster this relationship? _It takes time._
_**Proactively**_ _**share**_ your log and accomplishments, ask for specific feedback, and discuss professional goals in regular one-on-ones. They have a lot on their plates!
_**Show reliability!**_ Build trust by delivering what you agree on and providing updates when you don’t. You should be open, honest, and transparent to your comfort level. Hopefully, it will be mutual. Don’t forget to ask for help when needed!
_**This relationship isn’t only about you.**_ Take time to understand their goals and challenges. You might help each other, or you may learn more about team nuances to identify opportunities in the future. Try to handle a task weighing on the team, especially if it helps you grow. _Your manager is human; empathy can go a long way!_
#### 🐢 **pace yourself**
The “Stretching, Executing, Coast” method can help prevent burnout. These are states of being in your day-to-day. Navigate between them.
- **Stretching** can be hard but worth it! Initially, it is the most fun; you learn new things and grow quickly. You likely have fresh challenges to embrace!
- Extended periods lead to burnout or a loss of motivation. Surround yourself with trusted people to help identify if you’re pushing too hard for too long.
- **Executing** is your “normal” way of working and getting things done well. When you have the capacity to go beyond, do so, but in ways that don’t overstretch you.
- This is especially useful after a stretching period to avoid burnout.
- **Coast** when needed, but it should only be a few days. You may work at lower quality, not be proactive, and need task nudges in this state.
- It could be a breather after a tough project, catching up, or a mental break due to personal circumstances or symptoms of low motivation. If this extends, consider what must change ahead of a difficult conversation!
#### **🤔 owning your own career advice**
- Take a long-term view. Careers are not straightforward!
- Find your own happiness. Don’t let promotions and titles define your self-worth.
- Resist envy and avoid comparing yourself with others. It’s all about demonstrating impact when opportunities are not evenly spread out.
- Play well with others. Don’t “elbow” people out of the way. You will only benefit by getting along well with others and helping each other.
#### **🌱 thriving in different environments**
There are different modes of work environments. Learning to work in each has a well-rounded result as companies and teams may shift over time.
First, we’ll compare product and platform teams. Rotating between these builds empathy for various “customers.”
- **Product Teams** build products for external customers through quick validation cycles.
- Engineers are interested in the product and proactive with ideas and opinions. They are usually interested in the business, user behavior, and relevant data.
- They are curious about the “why” and build relationships with non-engineers through strong communication.
- End-to-end product feature ownership is common.
- **To thrive:** seek feedback, embrace curiosity, learn about the business, and build relationships.
- **Platform Teams** build functionality to support business-facing internal customers. They are key in high-growth engineering organizations.
- Engineers are focused on a technical mission often used by multiple teams.
- They are often experienced engineers who seek complexity with wider impact.
- Business impact can be harder to define. Frequently seen as a cost center, these teams are distant from external customers.
- **To thrive:** build empathy for internal customers, talk to (and work with) engineers using your platform, and aim for _some_ urgency.
Next, we’ll cover company modes: peacetime and wartime.
- **Peacetime** is a calm and steady state that focuses on expansion and reinforcing strengths. The company has a current market advantage!
- **Take your time**, complete your work with high quality, and focus on longer-term initiatives that will benefit the business.
- Avoid disputes and **foster relationships.**
- **Continue to learn and grow**; avoid stagnation, but still pace yourself.
- **Wartime** occurs when the company’s existence is at stake! Competition may be fierce, the core market turbulent, or an imminent threat may exist.
- **Get things done quickly.** Don’t focus on perfection.
- Don’t take conflicts personally; the strained context usually causes them.
- **Prioritize business needs.** Work like your job depends on it…it might!
- Pace yourself to avoid burnout!
## 🔧 building competence
The following are essential things to master to be considered a reliable and “competent” developer from the book's second part.
These things take time; keep pushing forward!
### 💪 getting things done
Competent engineers are good at breaking down work, providing realistic estimates, unblocking themselves, and delivering quality work.
How do they do this?
_**Focus on the single MOST important thing first, no exceptions.**_ Learn to say “no.” Necessary things arise, but this adds up. It’s a balancing act. I like the offered tactic of, “Yes, I’d like to help, but…” to provide context for your “no.”
_**Unblocking yourself**_ is a skill! Start to identify when you’re stuck, like spending more than 30-60 minutes without meaningful progress.
Leverage ideas to get unstuck:
- talk to a “rubber duck”
- draw it out
- read docs
- use an AI tool
- search online
- check forums
- take a break
- or start over
After a solid solo try, seek help! Asking for help is great, but come prepared to share what you’ve done to avoid wasting others’ time.
What if a person blocks you? This is more common at larger companies, and getting contact help is sensible. You may need to escalate delays, but handle this delicately to retain relationships!
_**Effectively breaking down work**_ takes thought. Begin at a high level and identify “chunks” of work. Then, narrow “chunks” into straightforward tasks. If they’re unclear, break them down again. Don’t be afraid to add, remove, or change tasks.
_**Estimations are challenging.**_ You will be asked. Use your (and peers’) experiences with the codebase, language, or similar work to provide estimates. When that’s not possible, prototype and timebox. You can also provide ideal and “worst case” options when there are unknowns, leaning toward the “worst case.”
_**Find mentors to help you grow.**_ This is a _group_ of people, not one person. You may look to more experienced engineers through a formal work program or informal, ad hoc one-on-one requests for help. Don’t forget about online mentors who share their experiences more publicly in blogs, podcasts, or books.
_**Retain a positive “goodwill balance”**_ and help others, too. You’ll need help, and this balance level spans a spectrum. You will have a higher balance when you first start (as a junior or during onboarding). People want to help! But, as you take, be sure to give back with your expertise. Give thanks either privately or in a public team setting.
_**Take initiative**_ when you can. Some of the most productive engineers take on work that wasn’t assigned! Talk with others to learn about projects and opportunities to volunteer. Try to document unclear things, take on investigations, research tools or frameworks your team uses, and talk with your manager about upcoming projects.
### ⌨️ coding
Practice. Practice more. You need to be proficient to translate your ideas into working code efficiently. Here are some ways to practice:
- _**Code regularly and ask for code reviews.**_ These are invaluable!
- _**Read as much as you write.**_ This helps avoid unusual habits, styles, and conventions. Check out open-source code for more reading opportunities!
- _**Code some more.**_ Build a side project, complete tutorials with coding exercises, do coding challenges, or complete code katas.
#### **👀 readable code**
Crafting code that others can read (and maintain) is equally important as code that is correct. What readable code actually is may vary by team, company, and language. Ultimately, if you and others you work with can easily understand it, then it’s readable!
You can practice this by revisiting the code you drafted to improve it before asking for a code review. When you receive feedback in code reviews, try to implement and understand them. Coding is a social activity; pay attention to the questions reviewers ask and ask follow-up questions. They might hold clues to improving readability!
#### ✅ **writing quality code**
Like all of the other advice in this section, this comes with practice. Some things to work on include using the correct levels of abstraction, handling errors well, and being wary of “unknown” states. In any case, you may have to experience these things to learn to do them well.
### 😎 software development
Wouldn’t you know it? As you go along, there are also software development-specific things you can work on!
#### 👌 **become proficient in a language**
Learn the fundamentals and work to understand advanced features. You can consult docs, find a good reference, look at code examples, study a book, or watch videos. Then go deeper! Learn about what happens “behind the scenes.” It’s recommended to go deeper before going more broad.
Master the “main” framework you use with a language. You can follow the same approach as learning a new language. Open-source frameworks have the added benefit of a visible codebase to see “under the hood.”
Once you have a good handle on one language, learn a second. It doesn’t have to be done arbitrarily; seek out helpful opportunities! AI as a tool can aid you to learn faster, too. Doing this helps you better compare language strengths and weaknesses and, hopefully, keeps you from repeatedly using the same language.
#### **🐞 debugging**
Get to know your IDE. Some of them have really powerful runtime debugging tools! You should also watch more experienced developers debug, perhaps during a paired debugging session.
Although it sounds counterintuitive, learning to debug without tools can be helpful. There’s the well-known console.log method, but you can also use paper to draw/write it out or write unit tests to help pinpoint issues.
#### **♻️ refactoring**
This also takes practice! Make this an everyday habit and work on it, starting with your own. After you write code, revisit it and see what you can improve. Some IDEs have refactoring capabilities. Look at yours and give it a shot if yours has this ability. Code reviews can give you more ideas for refactoring. It’s a good idea to give them a shot!
Reading through code should provide new insights: you can deepen your knowledge of the codebase and begin to spot inconsistencies. Make notes as you go along and seek feedback to see if a refactor is worthwhile before diving in. For an easier start, you could look at refactoring tests and the helpers for those tests. It will deepen your codebase understanding; ideally, others will be able to better understand.
#### **🧪 testing**
Speaking of tests, competent software developers ensure their code works, plain and simple. Before requesting a code review, they test the code. Preferably, your team will use automated testing to make this more seamless. In any case, reliable developers care deeply about edge cases and work to cover their work as much as possible.
### 🧰 tools of the productive software engineer
We’ve covered a lot already, but there are a few more things to consider.
- _**Your local development environment.**_ Get to know it well and become more efficient. Learn the ins and outs of your IDE or coding text editor of choice, like refactoring, compiling, running the project, hot reloading, debugging, running and debugging tests, or creating a PR. Configure your workflow, learn shortcuts, and set up formatting and linting, if needed. The smoother this is, the less context-switching you’ll have to do.
- _**Frequently used tools.**_ Get to know these and practice using them:
- Git - branching, rebasing, resolving conflicts and merging, cherry-picking
- Command line/terminal - often within many IDEs; start using it for tasks!
- Regular expressions - learn some; it can be useful
- SQL - learn the basics
- AI - give it a whirl to boost your productivity; there are inline coding assistants and generative AI chat interfaces to “talk through” concepts
- Company-specific developer tools
- A “productivity cheat sheet” - create your own doc full of references!
- _**Learn to iterate quickly.**_ Here are some ideas:
- Read existing code to understand what it does. Ask someone to walk through the structure, draw it out, share your code map, and create a cheat sheet to help you learn it.
- Make small code changes when possible.
- Learn how to debug the CI/CD for your project(s).
- Learn how to access production logs and dashboards.
- Run and write automated tests and checks.
- Don’t just wait for code reviews; ask for them!
- Get frequent feedback by building things!
- You can compare team member outputs but only to gauge iteration speed. Because career trajectories vary, don’t generally compare yourself to others.
**I hope that this information is concise and digestible while also being helpful!**
The book was excellent, filled with ample stories, examples, and actionable advice I didn’t cover that can be used at any career level in the current tech environment.
Get yourself a copy, too, to reference as you navigate your career!
## exploring typescript: runtime
URL: https://mindiweik.com/blog/exploring-typescript-runtime/
Published: 2024-08-20
This is part of a semi-monthly series that will put TypeScript under a microscope to become more adept overall. 🔬
Understanding the nitty gritty bits and pieces of a language can only benefit us as software builders!
**This post will cover runtime.**
1. 🤔 What is runtime?
2. 🦾 Reconstruct and confirm runtime types
3. 📚 Resources for further reading
**Let's go!**
## 🤔 what is runtime?
To start, let’s better grasp what type of “runtime” I’m referring to and what it actually means in that context.
Here, **runtime** refers to the process of a computer interpreting and performing a program’s instructions. Think of each line of code in your program or file as a line of instructions to be carried out!
In the first post of this series, I wrote about the [TypeScript Compiler](/blog/exploring-typescript-ts-compiler).
### _what does the compiler have to do with runtime?_
We learned that JavaScript code is generated from our TypeScript code through the compilation steps, which is what our runtime will use! JavaScript runtime is often executed in Node but could also be accomplished using Deno, Bun, or a web browser.
It’s interesting to note that TypeScript is statically typed. The types are checked at compile time, not runtime, like JavaScript or other dynamically typed languages. This process checks your code to help find syntax issues or correct misusage in advance.
Why is this helpful?
- This allows your IDE to offer some powerful tooling and reduces errors upfront!
- Issues can be caught before a user experiences something your team missed.
- You’ll achieve better readability and maintainability for your future self and teammates. When written well, TypeScript reads like good documentation.
- There should also be less cognitive load, scrolling, and searching for files; your IDE will show you relevant information when you mouse over variables!
### _what does runtime have to do with typescript, then?_
Simply put, TypeScript types don’t exist at runtime.
_**Come again?**_
Yes, that’s right. TypeScript types are “erasable,” removed from the compiled code. Interfaces and type annotations also fall under this umbrella of removed code. Although we haven’t covered [declaration files](https://www.typescriptlang.org/docs/handbook/2/type-declarations.html#dts-files), the types and interfaces described in these files will also disappear.
If you recall, we compile TypeScript code into JavaScript. TypeScript is a superset of JavaScript and adds more functionality _on top of_ JavaScript.
As a result, TypeScript-specific features and functionality disappear and can’t affect your JavaScript code. Therefore, interfaces, types, and type annotations cannot affect runtime behavior.
**That’s not at all to say that TypeScript is useless!** On the contrary, it empowers your JavaScript code output and developer experience if you take advantage of them, even if aspects disappear when your program hits runtime.
_**In my opinion, the biggest benefit of using TypeScript types is having a pre-defined “shape” of the types you work with.**_
This requires thoughtful intention! I’ve worked on projects that aren’t strict with typing and it caused me some headaches. When done well, defined shapes have clarified _exactly_ what I’m working with while building and developing.
As a simple example to play around with type “shape,” here we define the “shape” of an object we want to use to describe my three pets:
```js
// First, we'll define the shape
interface Pet {
name: string
age: number
type: 'cat' | 'dog'
}
// Then, we'll try to work with that shape!
const dog1: Pet = {
name: 'Rigby',
age: 6,
type: 'dog'
}
// This is valid
const cat1: Pet = {
name: 'Buzz',
age: 3,
type: 'cat'
}
// This is valid
const dog2: Pet = {
name: 'Rayla',
age: '6 months',
type: 'dog'
}
// This won't work!
// Type 'string' is not assignable to type 'number'.
const cat2: Pet = {
name: 'Imaginary',
type: 'cat'
}
// This won't work either, we're missing something!
// TypeScript complains here:
// Property 'age' is missing in type '{ name: string; type: "cat"; }' but required in type 'Pet'.
```
Before we run this code, we’ll encounter issues. In VSCode, for example, a little red squiggly identifies issues with the code snippet.
- We tried to use a string to describe `Rayla` as `'6 months'` instead of the expected number input for her age (`0`, `0.5`, or `1` depending on your own interpretation). Whoops! What were we thinking?
- Afterward, we created an imaginary cat. Because it’s not real, we aren’t sure how old it is! Bummer. Alas, `Pet` as an interface is looking for an age field.
### _wait, this example uses an interface, and that goes away after compile time, right?_
Correct; I’m so glad you brought that back to the forefront. Let’s look into how you can still ensure type safety at runtime!
## 🦾 reconstruct and confirm runtime types
Although TypeScript-specific features don’t exist at runtime, there are ways to ensure that runtime is safe. Don’t fret!
Here’s a great summary pulled from _[Effective TypeScript](https://www.oreilly.com/library/view/effective-typescript-2nd/9781098155056/)_ that is a quick way to describe some of what we’re about to cover:
> "TypeScript types are not available at runtime. To query a type at runtime, you need some way to reconstruct it. Tagged unions and property checking are common ways to do this."
If given the time to contemplate, one could probably come up with all sorts of ideas and examples. But here, we’ll cover 3 I use pretty frequently, plus examples.
1. Validate inputs from external sources
2. Check types or properties to handle in your code
3. Use a discriminated (or “tagged”) union
**Hint:** You can use the following commands to follow along with examples in #2 and #3 using TypeScript in Node!
```bash
tsc # compiles your file into JavaScript
node # runs your compiled code in Node
```
### **validate inputs from external sources**
You can usually find me building APIs with TypeScript.
This means we receive data from the outside world for most endpoints, which we have no control over, sent to us. Malicious actors may send questionable data to attempt to take advantage of our API!
I recommend validation because we honestly have no idea what a user (whether malicious or misinformed) may try to send us. You could build your own, but I’ve had success using [Zod](https://zod.dev/). It has excellent documentation and significant support. I’ve heard [Yup](https://github.com/jquense/yup) is also great, though I haven’t used it personally!
Using validation, we can check that the input is exactly what we need. It can even stop incorrect types in their tracks! At least in my experience with Zod, we can add all sorts of check layers to refine user input before our server fully interacts with it.
**Here are a couple of examples:**
```js
// Let's look at simple string inputs first:
// We want at least 1 character & trim excess white space for first name
// Say we don't require the last name, we can make it optional
// And we can use a built-in Zod email validator for the email!
const stringSchema = z.object({
firstName: z.string().min(1).trim(),
lastName: z.string().optional(),
email: z.string().email(),
})
// Next, let's take in a number and set up some boundaries!
// We want a positive number from 1-100 for some kind of code indicator.
// `gte` is an alias for minimum
// `lte` is an alias for maximum
const numberSchema = z.object({
code: z.number().positive().gte(1).lte(100),
})
```
Another great thing I’ve used with Zod is my own extra layer of refinement to validate the incoming data. Here are two slightly more involved examples:
```js
// Let's say we need an E164 format phone number string.
// We can add our own regular expression and check function for this:
const NORTH_AMERICAN_E164_PHONE_NUMBER_REGEX = /^\+1\d{10}$/
const isE164FormatPhoneNumber = (value: string): value is E164FormatPhoneNumber =>
NORTH_AMERICAN_E164_PHONE_NUMBER_REGEX.test(value)
// We'll use it in our schema, plus a helpful message for the user!
const phoneNumberSchema = z.object({
phoneNumber: z.string().refine(isE164FormatPhoneNumber, {
message: 'Should be a phone number in E164 format',
}),
})
// What about using an enum for 2 North American country codes?
// Users won't know about enums, but we can replicate that, too!
// This isn't a true enum; we'll cover this style in some future post.
const countryCodes = ['US', 'CA'] as const
const countrySchema = z.object({
isoCountry: z.enum(countryCodes),
})
```
…And there are all sorts of maneuvers like this you can use to check your data from the outside world to ensure it’s about as safe as you can manage - both for type safety and input security!
### **check types or properties to handle in your code**
When working with multiple potential types, it’s a good idea to confirm the input's “shape” or data type. This is great both in your TypeScript code and for runtime!
This could be a simple check when you expect, for instance, one type or another. Maybe we know a phone number could be provided as a string or a number, and let’s assume we know the input has the right character count or number length already, but we want the same [E164 format output](https://www.twilio.com/docs/glossary/what-e164) for a North American phone number in either case:
```js
const phoneNumberToE164String = (input: string | number): string => {
// We know we need to transform either kind to align with E164
let result = '+1'
if (typeof input === 'number') {
// input is definitely a number in this block
return result += input.toString()
} else if (typeof input === 'string') {
// input can only be a string in this block
return result += input
} else {
// Probably an unnecessary check, but something went wild
// if we get here we should handle
throw new Error('Invalid input')
}
}
const test = phoneNumberToE164String(5555555555) // A number is provided
console.log(test, typeof test) // +15555555555 string
```
In this way, although TypeScript types and true type checking won’t exist in the generated JavaScript, we can confirm which types we are working with and how we want to utilize them in a way that will translate into runtime. The generated JavaScipt code looks identical in this case!
We can use a similar concept with objects. Let’s say we’re now talking about storing art pieces in galleries!
```js
interface Art {
title: string
artist: string
paint?: 'acrylic' | 'watercolor' | 'oil' | 'other'
digital?: 'photo' | 'video' | 'slideshow' | 'other'
}
// Perhaps we want to handle these mediums differently:
// Paintings are stored in one gallery, digital art in another.
const paintingGallery: Art[] = []
const digitalGallery: Art[] = []
const moveArtworkToGallery = (artwork: Art) => {
if ('paint' in artwork) {
// Move a painting to the painting gallery
paintingGallery.push(artwork)
} else if ('digital' in artwork) {
// Move digital art to the digital gallery
digitalGallery.push(artwork)
} else {
// Again, unlikely, but this is unknown art we're handling!
throw new Error('We need a different gallery for this piece!')
}
}
moveArtworkToGallery({
title: 'The Starry Night',
artist: 'Vincent van Gogh',
paint: 'oil'
})
moveArtworkToGallery({
title: 'Self',
artist: 'Mindi',
digital: 'photo'
})
moveArtworkToGallery({
title: 'Two Calla Lilies on Pink',
artist: 'Georgia O\'Keeffe',
paint: 'watercolor',
});
console.log('Painting Display:', paintingGallery)
// list should have 2 paintings
console.log('Digital Display:', digitalGallery)
// list should have one photo
```
Again, the JavaScript code generated appears almost identical! The interface is the main element missing from the compiled code this time, but we can still determine a rough type using property checking in the `moveArtworkToGallery` function.
The property check only involves values that are available at runtime but still allows the type checker to refine the object's shape to the `Art` type. That’s great TypeScript code practice and translatable JavaScript all at once!
### **use a discriminated (or “tagged”) union**
This concept is similar to the object case above, and I’ve found it useful to specifically use the field name `type`, especially for building APIs. As a user I’ve seen it often in third-party APIs, so I feel it’s a common enough practice to lean on.
We are essentially doing property checking again, but in my experience, this is often with a set list of known types. Perhaps this list is described in the API documentation.
Let’s return back to the example of my three pets!
```js
// For reference, let's restate the interface shape:
interface Pet {
name: string
age: number
type: 'cat' | 'dog'
}
// In this example, we want to do act on the different types!
const makePetSound = (pet: Pet): string => {
if (pet.type === 'cat') {
return 'Meow!'
} else if (pet.type === 'dog') {
return 'Woof!'
} else {
throw new Error('Unknown pet type!')
}
}
const dog1: Pet = {
name: 'Rigby',
age: 6,
type: 'dog'
}
console.log(makePetSound(dog1)) // Woof!
```
When using a [discriminated union](https://dev.to/darkmavis1980/what-are-typescript-discriminated-unions-5hbb) (or “tagged” union as it’s described in _[Effective TypeScript](https://www.oreilly.com/library/view/effective-typescript-2nd/9781098155056/)_), we are essentially implementing some kind of type storage in our object using a “tag” that we can access and use at runtime.
How can we access this? It’s because there is also a value stored in the `type` field.
**I hope that these examples were helpful! Considering how your runtime will read and follow your instructions through your TyeScript code after compilation may help you build better!**
## 📚 resources for further reading
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
- [“TypeScript in 5 Minutes”](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html)
- [Wikipedia on TypeScript](https://en.wikipedia.org/wiki/TypeScript#:~:text=Development%20tools-,Compiler,that%20can%20execute%20the%20compiler.)
- [Wikipedia on Runtime]()
- [Contentful: “TypeScript vs. JavaScript: Explaining the differences”](https://www.contentful.com/blog/typescript-vs-javascript-explaining-the-differences/)
- [TotalTypeScript: “No, TypeScript Types Don’t Exist At Runtime”](https://www.totaltypescript.com/typescript-types-dont-exist-at-runtime)
- There are a few interesting exceptions to the rules here covered more in-depth: enums, namespaces, and parameter properties.
- [Log Rocket: “Methods for TypeScript runtime type checking”](https://blog.logrocket.com/methods-for-typescript-runtime-type-checking/)
- O’Reilly Books:
- [Programming TypeScript by Boris Cherny](https://www.oreilly.com/library/view/programming-typescript/9781492037644/)
- [Effective TypeScript by Dan Vanderkam](https://www.oreilly.com/library/view/effective-typescript-2nd/9781098155056/)
- [Learning TypeScript by Josh Goldberg](https://www.oreilly.com/library/view/learning-typescript/9781098110321/)
- I’m reading this as of this writing. I’ve seen him speak, and his humor and conciseness come across well in print!
- [TypeScript Cookbook by Stefan Baumgartner](https://www.oreilly.com/library/view/typescript-cookbook/9781098136642/)
- It was unread by me as of this writing, but it’s on my list.
## exploring typescript: ts compiler
URL: https://mindiweik.com/blog/exploring-typescript-ts-compiler/
Published: 2024-07-16
This is part of a semi-monthly series that will put TypeScript under a microscope to become more adept overall. 🔬
Understanding the nitty gritty bits and pieces of a language can only benefit us as software builders!
This post will cover the **TypeScript Compiler**.
1. 🤔 What is tsc (TypeScript Compiler)?
2. 🦾 How to best leverage the compiler as a tool
3. 📚 Resources for further reading
**Let's dive in!**
## 🤔 what is tsc (typescript compiler)?
TypeScript is free and [open-source](https://github.com/microsoft/TypeScript). Microsoft originally developed and released it in 2012. This language is often best used in larger or more complicated projects.
If you’re unaware, TypeScript is a superset of JavaScript that adds static typing to JavaScript syntax and functionality. This offers a level of type safety to help developers reduce mistakes, and, in my opinion, it broadens the context of the codebase, making it more readable for teammates and your future self!
_So, what the heck is a compiler?_
> "In computing, a compiler is a computer program that translates computer code written in one programming language (the source language) into another language (the target language)."
>
> [Wikipedia]()
In our case, TypeScript is being compiled into the higher-level language of JavaScript to be understood and used by browsers or a Node environment. Once that’s completed, the JavaScript version of your project can then be deployed.
TypeScript project steps
01 · typescript
Write code in TypeScript
→
02 · compile
Compile code to JavaScript
→
03 · javascript
Deploy code for browsers or Node
_Note that this differs from transpiling, which we will discuss in a future post._
Technically, other compiler options, like Babel, could be used. However, in my own experience, I’ve most often seen the TypeScript Compiler. In any case, we need to convert TypeScript code to JavaScript code!
_Oh, and did you know that_ tsc _is actually also written in TypeScript and compiled into JavaScript?_
🤯 I know, it blew my mind, too, when I heard [Josh Goldberg](https://www.linkedin.com/in/joshuakgoldbergcodes/) mention this in a recent talk I heard!
## 🦾 how to best leverage tsc as a tool
### **okay, I think I get it, so how do I use it? 🛠️**
Well, to start, you need to ensure you [install TypeScript](https://www.typescriptlang.org/download). You can do this globally:
```bash
npm install -g typescript
```
For a specific project, navigate to your project folder in the terminal or your favorite IDE:
```bash
npm install typescript --save-dev
```
Then, you can run the TypeScript compile command in the terminal:
```bash
tsc
```
The above command will perform the compilation step of the TypeScript file and output a compiled JavaScript file with a similar name and a .js file extension: ``
Overall, it’s pretty simple to use, and I use it often in my package.json scripts! If you want to get a little “fancy,” you can also make use of the many tsc [CLI options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) in your scripts or directly in the terminal.
### **`tsc` is installed, and I can generate javascript files! but how can I have more control over the typescript compiler? ⚙️**
The best way to manage your compiler output (as well as other TypeScript usage details) is through the tsconfig.json file. You can set up the TS project and this special file using the [command](https://www.typescriptlang.org/docs/handbook/compiler-options.html#:~:text=Initializes%20a%20TypeScript%20project%20and%20creates%20a%20tsconfig.json%20file.):
```bash
tsc --init
```
This file is - you probably guessed it - a JSON file stored in the root level of a TS project. Here, you can determine specific runtime mechanic choices for your unique project using the various [options](https://www.typescriptlang.org/tsconfig/) available.
You can set up your project details, such as whether or not you want “strict” checks, what JavaScript features you want supported in the compiled version, and which project files to include or exclude.
There are so many configurations you can use! In fact, each of the various TS projects I’ve worked on has a uniquely different tsconfig file based on the needs of the team and the project.
### **anything else I should know? ⛓️**
You’re not limited to one single tsconfig file!
Perhaps your overall project needs unique frontend and backend settings. Similarly, if your project supports browsers and Node a bit separately, you might want to create different compiler output files for better support in each environment. Or, if you want to create a debugger setup that maps the files (see example below) but don’t want this to happen for your typical build, it could be a good idea to have a separate tsconfig for this process.
I could keep brainstorming, but I think you get the point.
_So, how does one extend a_ tsconfig _file or use more than one one?_
Put simply, use the extends option and reference the base file.
Here is a simplified file example from my own usage, too:
```js
// Base file: tsconfig.json
{
"compilerOptions": {
"target": "es2020",
"module": "CommonJS",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"skipLibCheck": false
},
"exclude": [
"node_modules/**/*",
]
}
```
Then, we have a debugger setup in which we want to ensure everything else is the same, but in this case, we want source mapping to be used:
```js
// Debugger tsconfig file extends the base file:
// tsconfig.debug.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": true
}
}
```
When we want to run the debugger, this extended tsconfig file is used on top of the original!
## 📚 resources for further reading
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
- `tsc` / [Compiler section](https://www.typescriptlang.org/docs/handbook/2/basic-types.html#tsc-the-typescript-compiler)
- [“TypeScript in 5 Minutes”](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html)
- [tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)
- [Wikipedia on TypeScript](https://en.wikipedia.org/wiki/TypeScript#:~:text=Development%20tools-,Compiler,that%20can%20execute%20the%20compiler.)
- [Wikipedia on Compiler](https://en.wikipedia.org/wiki/Compiler)
- [Contentful article](https://www.contentful.com/blog/what-is-typescript-and-why-should-you-use-it/)
- [Visual Studio Code tsconfig details](https://code.visualstudio.com/docs/typescript/typescript-compiling#_tsconfigjson)
- O’Reilly Books:
- [Programming TypeScript by Boris Cherny](https://www.oreilly.com/library/view/programming-typescript/9781492037644/)
- [Effective TypeScript by Dan Vanderkam](https://www.oreilly.com/library/view/effective-typescript-2nd/9781098155056/)
- A [snippet](https://www.oreilly.com/library/view/effective-typescript/9781492053736/ch01.html#:~:text=TypeScript%20is%20a%20bit%20unusual,that%20runs%2C%20not%20your%20TypeScript.) from this book via O’Reilly
- [Learning TypeScript by Josh Goldberg](https://www.oreilly.com/library/view/learning-typescript/9781098110321/)
- I haven’t read it as of this writing, but I’ve seen him speak a few times, and I’m excited to start it after wrapping up a couple of other books!
- [TypeScript Cookbook by Stefan Baumgartner](https://www.oreilly.com/library/view/typescript-cookbook/9781098136642/)
- I haven’t read it as of this writing, but I’ve heard good things and it’s on my list.
## 3 big, scary software engineering words explained
URL: https://mindiweik.com/blog/3-big-scary-software-engineering-words-explained/
Published: 2024-06-25
A simple Google search often clarifies the meaning of unknown terms I encounter as a Software Engineer. Sometimes, though, they seem more complicated and require a second (or even third or fourth) look to understand them better.
We’ll cover three of these terms that have given me past trouble. I hope I can reduce your search just a smidge! 🙃
**Here we’ll cover:**
- What are these words?
- What do they mean to a Software Engineer?
- And what is an example to point to?
**Let’s dive in!**
## ♻️ idempotent
**Simplified Meaning:**
- The **same result is produced every time** an operation with a particular input is submitted as if it were only submitted once.
**Use case:**
- This typically refers to HTTP `GET`, `PUT`, or `DELETE` requests. This ensures reliability, consistency, and fault tolerance when working with a [RESTful API](https://www.redhat.com/en/topics/api/what-is-a-rest-api).
- `GET` is technically idempotent because it retrieves and does not change a resource.
- If the request is incomplete due to an error or issue, the client can resubmit the request to achieve the desired end result.
**Example:**
- A client submits a `PUT` request to change a user’s [E164 format](https://www.twilio.com/docs/glossary/what-e164) phone number from its original:
```json
{
"name": "Mindi",
"email": "mindi@test.com",
"phone": "+18885551234"
}
```
…to:
```json
{
"name": "Mindi",
"email": "mindi@test.com",
"phone": "+18885550001"
}
```
- If an unexpected error occurs at any level of the process, the client should be able to resubmit the request and expect the same result.
- No matter how often the client submits this exact `PUT` request, even if the user goes wild and submits 100 times, the E164 format phone number should ultimately reflect the desired update `+18885550001`.
**Useful resources:**
- [Wikipedia](https://en.wikipedia.org/wiki/Idempotence#:~:text=which%20is%20idempotent.-,Computer%20science%20meaning,-%5Bedit%5D)
- [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Glossary/Idempotent)
- [REST API Tutorial: Idempotent REST API](https://restfulapi.net/idempotent-rest-apis/)
- [VIDEO: Alex Hyett](https://www.youtube.com/watch?v=XAccGbtl3Z8)
## 🔀 polymorphism
**Simplified Meaning:**
- Different objects can be treated as common, enabling **the same operation to work uniquely on different objects**.
- Ultimately, it refers to a programming aspect’s ability to appear similar but operate differently in certain scenarios.
**Use case:**
- Method overloading (see below example).
- Accept various function parameters.
- For instance, if a function can accept an `id` that is either an integer or a string and operate upon it properly after determining which it has received.
- In my research, this can also apply to things like TypeScript types and interfaces.
- Using shared, common functionality allows for more flexible, scalable, and maintainable code long-term.
> A common use of polymorphism in OOP is when a parent class reference is used to refer to a child class object. - [Margaret Rouse](https://www.techopedia.com/contributors/margaret-rouse), Techopedia
**Example:**
- Here, let’s do something simple. We’ll use Python to make a base `Animal` class and `Dog` and `Cat` derived classes to overwrite a core `Animal` class method.
```python
class Animal:
def sound(self):
return "Each animal makes a unique sound!"
class Dog(Animal):
def sound(self):
return "woof woof"
class Cat(Animal):
def sound(self):
return "meow meow"
```
- When we create a function that can use any of these classes, we will see that an extensible class allows us to get the expected output from our cat or dog.
```python
def animal_sound(critter):
print(critter.sound())
# Create instances of Dog and Cat
dog = Dog()
cat = Cat()
# Call the animal_sound function with different types of animals
animal_sound(dog) # Outputs: woof woof
animal_sound(cat) # Outputs: meow meow
```
**Useful resources:**
- [Wikipedia]()
- [Techopedia](https://www.techopedia.com/definition/28106/polymorphism-general-programming#:~:text=With%20polymorphism%2C%20each%20subclass%20may,displaying%20trotting%20on%20the%20screen.)
- [Tech Target](https://www.techtarget.com/whatis/definition/polymorphism#:~:text=The%20word%20polymorphism%20is%20derived,biology%2C%20chemistry%20and%20drug%20development.)
- [VIDEO: iAmDev](https://www.youtube.com/watch?v=tIWm3I_Zu7I)
## 💻 isomorphic [javascript]
**Simplified Meaning:**
- JS code is written strategically to **run on the client OR the server**.
- The project must maintain a minimum Node version and work on multiple browsers. Transpilers and polyfill tools implement modern JS features that may be missing, making this challenge a little easier!
- Typically, you will use third-party libraries when writing your JS code and avoid using native Node libraries or browser APIs.
- General isomorphism in programming is slightly different ([Stack Overflow link](https://stackoverflow.com/questions/11245183/importance-of-isomorphic-functions), for edification).
- This is when different programming structures/processes can be transformed into one another without losing info or functionality. They are essentially the same in structure or behavior but may look different.
**Use case:**
- A file or code snippet that can be run in either instance, improving modularity.
- A project using server-side rendering to increase app performance.
- This isn’t magic - there may be other performance drawbacks.
- This can also provide crawlers access to content to improve SEO.
- Reduce discrepancies and potential bugs with consistent logic and rendering.
**Example:**
- Let’s do something super simple. We’ll create a snippet to inform you which environment is running.
```js
// isomorphic.js
const defineEnvironment = () => {
if (typeof window == 'undefined') {
console.log('This is the server!');
} else {
console.log('This is the browser!');
}
};
defineEnvironment();
```
- To run this in Node:
- save the code
- navigate to the file via a terminal or command prompt
- run the script: `node isomorphic.js`
- you should see “This is the server!”
- To run this in the browser:
- open a browser’s dev tools
- Google Chrome on a Mac = command + option + J
- navigate to the console
- paste the content of the `isomorphic.js` file into the console and run it
- you should see “This is the browser!”
**Useful resources:**
- [Wikipedia](https://en.wikipedia.org/wiki/Isomorphism)
- [Medium Article from AirbnbEng](https://medium.com/airbnb-engineering/isomorphic-javascript-the-future-of-web-apps-10882b7a2ebc#.4nyzv6jea)
- [GitHub repo examples](https://github.com/topics/isomorphic-javascript)
- I have not vetted these, but they seem interesting!
- [VIDEO: PortEXE](https://www.youtube.com/watch?v=tVaFAAAzHsw)
**Do you feel at least slightly more confident with these words now?**
**I sure hope so!** 🤭
## experiences with a local gitlab runner: part2
URL: https://mindiweik.com/blog/experiences-with-a-local-gitlab-runner-part2/
Published: 2024-06-11
_We won't discuss CI/CD or setting up a GitLab Runner here. For this information, please refer to [Part 1.](/blog/experiences-with-a-local-gitlab-runner-part-1)_
**Quick recap:** I wanted to set up a CI pipeline to improve the team’s development speed while working on a side project with my team. I also wanted to do simple checks to ensure we wouldn't break anything obvious when merging to main!
Let’s find out how it went after the initial setup…
**Here, we’ll cover:**
1. 🧩 First Local Success and Challenge
2. 🥧 Move to Raspberry Pi
3. 🔄 Current Usage
4. 💡 Lessons Learned
## 🧩 first local success and challenge
**Success:** If you read [Part 1](/blog/experiences-with-a-local-gitlab-runner-part-1), you’ll find that I successfully learned how to set up a local GitLab Runner on my MacBook!
The CI pipeline ran smoothly and caught a few issues before merging to main. It was doing its job. 🎉
**Challenge:** The GitLab Runner on my Mac took over my computing resources for local development.
I manually started and stopped the runner when merge requests came through.
This was not ideal! I wanted to move the Runner to a [Raspberry Pi 4](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/) I hadn’t used for anything specific yet. _(Thank you to my partner for gifting this to me early in my coding journey!)_ This way, I could keep the Runner open for quicker development while freeing up my resources.
**Sure, we could have chosen other options for a Runner**, including using the GitLab built-in Runners. But I’ve wanted to find a good use for this Raspberry Pi, and I was determined to make it work!
## 🥧 move to raspberry pi
The Raspberry Pi hadn’t been booted up since pre-pandemic times. It had armv7l architecture and Raspian Stretch as the OS. By this time, both were quite outdated.
After spending [way too much] time trying and failing to get the GitLab Runner installed and coordinated on the new machine, I found a [GitHub issue](https://github.com/aredridel/node-bin-gen/issues/59) that pointed to why npm and node weren’t working properly on the Pi. Bonus ✨ there was a familiar name from a Slack group for women in tech I engage with!
In my research, I soon discovered that the Pi also needed to upgrade to Raspian Bullseye, the updated Raspberry Pi OS at the time of this project. Of course, I now know that update alone wouldn’t fix the outdated architecture.
Directions in a blog were followed, and despite the clear warnings of a “dangerous” CLI command, I corrupted the Pi. Before taking the risk, I double-checked that I had nothing important to lose. 👍
**It turns out this “step back” was a great thing!** It forced me to learn to reset the Raspberry Pi through the [Operating system images](https://www.raspberrypi.com/software/operating-systems/) and start from scratch. This allowed me to update to the most updated arm64 architecture and Bullseye OS in one fell swoop.
Finally, I completed the GitLab Runner setup, connected everything, and watched the first pipeline run. I was ready to celebrate! 😅
_Of course, there was yet one more challenge to handle…_
The frontend build couldn’t pass because OSX is the [only system that ignores file case sensitivity](https://medium.com/@paulbjensen/what-mac-oss-case-insensitive-filenames-teaches-us-cd8feee7b0b3). I wasn’t aware of this until trying to get our project working alongside Linux!
Our team develops on Mac devices, and the Raspberry Pi is a Linux/Raspian device, so of course this needed to be addressed to move forward. This is especially needed because we’re planning to deploy this project in the coming months and it will likely be on a Linux AWS instance. At least this aspect should be resolved in advance. 🤞
To combat this, I installed the [case-sensitive-paths-webpack-plugin](https://www.npmjs.com/package/case-sensitive-paths-webpack-plugin) npm package, updated our webpack config file for the front end, and worked through some file and image renaming challenges.
We’re using TypeScript, and I also needed to add a d.ts [types file](https://www.typescriptlang.org/docs/handbook/2/type-declarations.html) for our image files to work properly.
And THEN, it was safe to celebrate when the pipeline jobs turned green again while using the Raspberry Pi Gitlab Runner! ✅
## 🔄 current usage
**TL;DR: Things are running smoothly! 🙌**
The Raspberry Pi is plugged in and sits to the side of my office workspace in a position that limits fan noise.
The Runner is always open because computing resources are now dedicated to this purpose. This means that our merge requests always go right through the CI pipeline - this happens automatically and without any thought!
After getting everything set up properly, I’ve had to move the device's location a few times. Resetting the machine or reconnecting to Wi-Fi during these changes wasn’t as much of a hinderance as I expected. It all reconnects without much issue, and it’s easy to connect a monitor and keyboard/mouse to interact as needed.
Otherwise, the Raspberry Pi is essentially left running with limited effort and power usage. We recently installed solar panels that can cover more than our household usage; I don’t feel I’m wasting energy keeping a small server running! ⚡️
## 💡 lessons learned
This second part of the journey certainly had ups and downs. I gave a quick snapshot above, but there was so much time spent reading articles, documentation, and tutorials, plus ample Google searching to try to pinpoint the right methods to make everything work well together.
**I have 3 key takeaways from this portion of the local GitLab Runner experience:**
1. Sometimes, a setback can propel your overall development.
2. Don’t forget to read beyond the surface details.
3. Keep going, even when it’s hard. The reward awaits!
### sometimes, a setback can propel overall development.
There’s such a thing as the [sunk cost fallacy](https://thedecisionlab.com/biases/the-sunk-cost-fallacy). I’m sure you’ve heard of it. I fell into this trap, too, and didn’t want to pivot after I invested a lot of time on a solution already.
When I corrupted my Pi it actually forced me to take a step back and reconsider the problem. I had to start fresh with a new architecture and OS and this turned out to be more helpful than I initially realized.
Without being stopped in my tracks, I may have kept banging my head against the wall trying to get the unsupported architecture to work (countless hours had already been paid toward this battle). I didn’t want to give up!
But it was never going to work, honestly. Starting over gave me a new perspective that allowed me to get where I needed to be much faster than I likely would have otherwise.
### don’t forget to read beyond the surface details.
Errors always provide clues. It’s not that you have to read between the lines, but read them carefully!
I hate to admit it, but I struggled to resolve the issues with the unsupported Pi architecture and the Mac/Linux case-sensitivity conundrum for far too long. I half-read the error messages and assumed I understood what they meant. Then I went around in circles trying to solve for things that actually weren’t even close to resolving my issues in hindsight.
Always, always, always read the messages thoroughly and don’t be afraid to Google your exact error.
Some of the hits may not match exactly what you need for your own situation, but often I’ve found that reading others’ results can help point to the right direction of what to look into next to try to solve my particular brand of the issue.
### **keep going, even when it’s hard. the reward awaits!**
Although this migration to the Raspberry Pi took time and effort, it’s been one of my happiest and more rewarding moments as a software engineer.
I absolutely love a challenge. The harder to unravel, the more I want to see the end result. And overall, this one was a doozy for me.
Even when I would make some progress, I would hit another wall. It was hard, but I wanted to see this conclusion so I kept pushing myself. _And, my partner helped keep my spirits up when things got hard. I appreciated that!_
**Once I finally got everything working smoothly, I celebrated that hard work**. Now, I smile a little when I hear the fan and know it’s working behind the scenes or when I submit a PR and it quickly hits the pipeline to show that satisfying green checkmark. 🥹
## the power of career change
URL: https://mindiweik.com/blog/the-power-of-career-change/
Published: 2024-05-28
_**This post was originally written for Women Who Code and was selected for publication! However, in unfortunate timing, my post was scheduled for May 22, 2024 and Women Who Code shuttered the organization in April 2024.**_
_**So, I decided to make some minor edits (to open the audience outside of just women in tech) and share it instead! The format is slightly different, but overall similar.**_
_**Please enjoy and let me know if you have any questions or if I missed anything!**_
I found tech later in life, as many others have. Let’s start with a brief career snapshot as a baseline for examples of how to use prior experiences to leapfrog into success as a career changer:
- Undergrad communication degree: public speaking, PR, interpersonal and organizational communication
- Non-profit program director and aquatics director
- The long hours and hard finances quickly burned me out.
- Real estate assistant
- My boss and I created a real estate marketing “side hustle.”
- Side hustles
- A creative outlet: licensed drone pilot, built websites, photographed homes, created print materials, and operated a 3D camera.
- Gig work: home showings, attended inspections/contractor meetings.
- COVID-19 challenges and contemplation
- Real estate and marketing weren’t for me. “Solopreneur” contract work is unstable.
- Tech drew me in; I tried self-learning to code.
- Small, remote SaaS company building real estate tools/websites
- Start in Support, take every technical opportunity to slingshot my way to the technical side.
- After two years of trying to code on my own, I needed the accountability of a bootcamp to reach a higher technical level.
- Year four with the same company
- I was a Platform Expert and Onboarding Manager and then I moved into a Software Engineer role last May!
## **lessons learned**
I’ve learned much and the things that went well for me others can now use to thrive! Here are five tips from my journey.
1. Identify your “foot in the door”
2. Use expertise in new pursuits
3. Weaknesses are growth opportunities
4. Allow experiences to navigate
5. Intentional work, better than hard work
## **#1 - identify your “foot in the door”**
Consider whether current or prior industry experiences can open opportunities. It may be the boost needed to get in - or closer to - the end goal.
My first career took advantage of eight years as a young adult lifeguard. Combined with leadership opportunities in college, I proved I could run an aquatics facility and its programs.
Later, I landed my first tech role through real estate experience. It sounds counterintuitive, but relating with and translating for customers allowed more technical opportunities. I could liaise between customers, product, and engineering teams for new features or customer issues.
This leads us to the next tip!
## **#2 - use expertise in new pursuits**
Expertise is composed differently depending on the background. Pinpoint your skills and harness them to propel a new career!
I honed customer and team communication in multiple roles, team training from aquatics, and project management from real estate.
Entering tech through Support, I deeply leveraged my communication skills. I frequently took opportunities to train my teammates and organized work using deadlines and project management. As an Onboarding Manager, I trained my team in-depth on the system and maneuvered project management to automate old processes.
These skills help in my current role as a software engineer, too! I have a unique empathy for our customers and their needs. I collaborate with teams. I find opportunities to “train” on concepts I'm learning or demo work. And I directly contribute to our project planning.
## **#3 - weaknesses are growth opportunities**
Take stock of where you need work. We all have something! For me, this is patience and what I call ambiguous ambition.
My weakness appears when I must bring others up to speed or if things I hadn’t considered could impede a plan or project. I’m not reckless, but it’s an area for improvement.
Especially in the beginning, I trekked without definite goals. I didn’t want to grow my career in the non-profit or aquatics sphere. There were chances for higher roles of responsibility, but I was exhausted.
It feels to me looking back I spent far too much time as a real estate assistant. I was unable to grow, and I left dissatisfied with my trajectory. This stall led me to seek an alternative, bringing me to tech. I’m frankly back where I was in the early days, feeling overwhelmed about my long-term options now that I have so many paths! So, I must remember this.
The important thing is to identify these areas and self-honesty about the challenges and skills to practice when seeking a career change. I keep practicing to grow personally, professionally, technically, and as a leader. There’s no need to be overly self-critical, but awareness is helpful.
## **#4 - allow experiences to navigate**
Decisions and unexpected opportunities may arise. Gauge whether they’re worth taking or align with your direction. This was my situation when I became the Onboarding Manager. It felt backward from the technical role I wanted, and I was open with the company about this.
I spoke with leadership early and often. They supported my end goal career shift because I brought up my desire frequently and clearly.
In the meantime, they requested I apply my platform and customer knowledge to guide my new team while adding tremendous efficiency by automating our manual processes. I decided to accept these adjacent technical challenges while working through my bootcamp during nights and weekends.
Other times, you might determine it’s time for a shift. After reaching burnout several years into a non-profit atmosphere, it was time to move on. Real estate wasn’t my first choice, but I saw an opportunity and allowed my experiences to guide me.
These decisions are very personal. Trust in yourself, your wants, and your goals are important. I hope that these stories are helpful.
## **#5 - intentional work, better than hard work**
It’s easy to fall into working hard without direction. [Take it from Arit!](https://www.youtube.com/watch?v=pe5fb4t_JDw) _(← A Women Who Code video ~1 hour long with some tremendously useful stories and advice!)_
I worked hard as a real estate assistant. When I first entered tech, I worked hard in Support, trying to prove - mainly to myself - that if I worked harder, I would become more technical.
Recognition came and felt nice, but it wasn’t getting me closer. Ambiguous ambition was also at play. It seemed easier to keep going than to take the intentional pause to consider what I truly needed.
Once I took time to explore, I made clear decisions on a direct path. Real estate went nowhere fast; I enjoyed tech and wanted to be in that space. When I approached burnout in Support, I found that pointed help would get me to the next phase.
The decisions were suddenly easy. I got closer to a fulfilling career. Take a step back to get clear!
I hope my experience can help any career changer; being thoughtful can help amplify a career.
## feeling inadequate is okay.
URL: https://mindiweik.com/blog/feeling-inadequate-is-okay/
Published: 2024-04-23
If you haven’t heard that working in tech can bring out feelings of inadequacy or imposter syndrome, you may live under a rock. It’s par for the course in engineering. I’ve heard warnings about it hundreds of times since entering tech.
I thought I knew the signs and all the approaches to avoid it.
My “can-do” attitude would never steer me into this void…right?
Yet, here I am, struggling over the last few weeks with where I feel my experience is now and where I feel like I should be or _wanted_ to be at this stage in my overall career and my tech career since transitioning to engineering.
On top of this was the loss of a community last week that I have enjoyed and has uplifted me to engage with other empowering people as an underrepresented person in tech. [Women Who Code](https://womenwhocode.com/) recently [announced the shuttering of the organization](https://womenwhocode.com/blog/the-end-of-an-era-women-who-code-closing). It is a significant blow for women around the globe, myself included.
I want to share 3 resources for identifying and working through these challenges that have recently helped me.
1. 🔗 A link from someone who cares
2. 📨 A well-timed post from a newsletter subscription
3. 🎧 A truly excellent podcast episode
## 🔗 a link from someone who cares
One of the best ways to work through these challenges is to talk to someone who can hear you, relate, and help you put things into perspective. I’m thankful for the support around me. My partner is patient and kind and grapples with these things, too, as they are well into their tech career and have been here before.
We spoke about this at length, and yesterday, they sent me a link to a post written by Dr Milan Milanović: [How to Fight Imposter Syndrome](https://newsletter.techworld-with-milan.com/p/how-to-fight-impostor-syndrome).
An excellent reminder that these challenges have almost limitless variation; they all boil down to feeling some degree of inadequacy. For me, right now, it’s the section “The More You Know, The More You Realize You Don’t Know.”
I hoped to have had more experiences and learned more by this time. As a career changer with 10 years of prior experience, I knew there would be a learning curve, starting at the beginning again has bee hard.
During work hours, I learn between tasks. I read articles and write a lot in my free time to help new knowledge stick. I joined a side project to fill in gaps and push further than I can experience in my workday to accelerate myself.
But learning takes time. And I’m burnt out. After a short break, perseverance is the only option because I won’t give up.
**The list of recommendations at the end is an excellent resource for working through these challenges if you’re in this funk, too.**
## 📨 a well-timed post from a newsletter subscription
This week, I received a newsletter in my inbox that was serendipitously timed with these current experiences.
Gregor Ojstersek shared [Going from impostor one day to feeling like a superhuman the next day](https://newsletter.eng-leadership.com/p/going-from-impostor-one-day-to-feeling?utm_source=post-email-title&publication_id=1115815&post_id=143792451&utm_campaign=email-post-title&isFreemail=true&r=29u7hv&triedRedirect=true&utm_medium=email)
His post was a great reminder that this will come and go. Even after 10 years in the industry he experiences these challenges!
My biggest takeaway, is that these periods of discomfort are excellent growth opportunities. Identifying this is crucial because you can learn strategies to get through the tough times for long-term improvement.
Everyone feels like an impostor sometimes. It’s a sign that we are growing and learning. What’s really important is that we embrace it and control it so that it doesn’t get to us.
It doesn’t feel great right now, but if we keep going, our destination will most likely surprise us!
## 🎧 a truly excellent podcast episode
Although Women Who Code is closing down, I’ve learned a lot from the experiences, including the podcast they’ve run for the last several years, one episode in particular hits home during these times.
“Career Nav #13: Using Conflict Fluency Skills to Create Inclusive Workspaces” with guest [Noelle Notermann](https://www.linkedin.com/in/noellenotermann/):
It was initially called the “Imposter phenomenon” in the 1970s research.
_**Why is this important?**_
“Syndrome” indicates a long-term experience, whereas a “phenomenon” is short and fleeting. These feelings may come and go, but they are typically short in the grand scheme of the overall experience, and this discomfort won’t last forever.
Additionally, this episode provides many tangible concepts and helps put the typical “imposter syndrome” feelings into perspective. It’s a great listen if you can get to it sooner rather than later, I can’t guarantee the podcast will live on.
## conclusion
Acknowledging and identifying the discomfort is the first step.
Working through the challenges we face is the hardest part.
If we keep going, the efforts are well worth it. And they become more manageable each time the struggles arise as we develop more tools to persevere.
If you’re with me, keep going! I will, too. 🫶
If you’re not here now, return for a read when needed. 🙌
## learning typescript
URL: https://mindiweik.com/blog/learning-typescript/
Published: 2024-04-16
My initial developer training consisted of mainly JavaScript. When I began a role using TypeScript, I wanted to learn best practices and how to work well as quickly as possible.
That said, true learning takes time. I have to remember to be patient and that practice makes me better each time I do it! I’m about a year in and I wanted to share what’s helped me the most during this time.
The base knowledge of JavaScript helped a lot with learning TypeScript as it’s a superset of JavaScript. It’s open-source and written/maintained by Microsoft. It’s pretty powerful in my experience.
However, there are a lot of nuances and unexpected cases within TypeScript I found interesting along the way.
We'll talk through my core learning materials and suggestions:
1. 👩💻 Scrimba
2. 📖 Programming TypeScript by Boris Cherny
3. 🤓 Effective TypeScript by Dan Vanderkam
4. 💻 Udemy: TypeScript 5 for Developers by Alex Dan
5. 💪 Lots of Practice
## 👩💻 scrimba
When I first started, I sought an interactive way to learn, which is how many best absorb knowledge. I found this in Scrimba!
[Scrimba](https://scrimba.com/allcourses) is a really cool platform for learning. Watch a video while also toying with the examples directly in the embedded code editor! It’s a new and engaging way to go through coding tutorials while also getting your hands dirty, so to speak.
I found a course here called “[Learn TypeScript](https://scrimba.com/learn/typescript)” by [Ania Kubow](https://www.linkedin.com/in/ania-kubow/?originalSubdomain=uk). I’ve followed some of her [YouTube tutorials and courses](https://www.youtube.com/@AniaKubow) before, so she was a welcome and familiar face for me!
I’ve taken a look at other Scrimba courses since taking this particular course and I’ve enjoyed a majority of those I’ve worked on or experienced.
## 📖 programming typescript by boris cherny
A teammate sent this book to me and I was so grateful. There are so many beginner-friendly concepts here that were helpful.

Even though I had been working with TypeScript for a couple of months when I received the book, these concepts were essential to solidifying my foundation. Even the seemingly simple details that are core to the functionality have deeper descriptions embedded and some helpful use cases to build better understanding.
As the book moves forward, more advanced concepts and examples are provided, too, so regardless of level of experience with TypeScript there is sure to be something for everyone to take away!
## 🤓 effective typescript by dan vanderkam
I felt this book had more tangible and actionable advice, tips, and examples. _Programming TypeScript_ felt better at providing that aforementioned foundation and underlying concepts.

This book was also broken into 62 very specific ways to get better at TypeScript, making it very easy to pick up, read one “item,” and then put it down to practice it.
I’ve also [mentioned on LinkedIn](https://www.linkedin.com/posts/mindiweik_todoist-a-to-do-list-to-organize-your-work-activity-7179515177124462594-jaF3?utm_source=share&utm_medium=member_desktop) that I use [ToDoist](https://todoist.com/). Once I figured out the groove and commitment, I was able to put in a (mostly) daily task to complete an “item” each day. This helped keep motivation to move toward completion when it was in such digestible chunks!
I’ll refer back to this book, in particular, to take a look at it’s very specific examples and suggestions when I run into any issues or need inspiration to solve a problem.
## 💻 udemy: typescript 5 for developers by alex dan
Generally, I appreciate repetition in my learning.
I found [TypeScript 5 for Developers](https://www.udemy.com/course/typescript-full-stack-programming/?couponCode=ST8MT40924) on [Udemy](https://www.udemy.com/) by [Alex Dan](https://www.linkedin.com/in/alex-dan-02598a137?miniProfileUrn=urn%3Ali%3Afs_miniProfile%3AACoAACFtJ50B6aaQ7qIcbN88GJz13jVmTL9AQRY&lipi=urn%3Ali%3Apage%3Ad_flagship3_search_srp_all%3Bs89i5unCQlia%2F4aPZ4hsEA%3D%3D). I took this course over the same time I was reading _Programming TypeScript_ and _Effective TypeScript_ and it happened to time out well to reinforce my learnings!
I would read about a concept and then within some short time, I would come across a similar concept in the course…or sometimes the other way around.
In this way, I was hitting all the bases for learning: repetition of concepts, read it statically, watch it “live” in a code editor, hear about it from a knowledgeable source, and practice the concepts in my daily work through small, local code edit tests.
I haven’t done a review of this course, like I did with the [Fundamentals of Backend Engineering](/blog/fundamentals-of-backend-engineering-course-review) course, but I would give it a similar review. The value was high (I also actually bought the TypeScript course in the same sale), the content was manageable and informative, and I learned a lot!

## 💪 lots of practice
This should not be understated! Practice makes perfect and there is a reason why the cliche exists. If anything, this was the _**most**_ helpful aspect of my learning journey and I encourage everyone to take this seriously in their own journey.
When I first started using TypeScript at work, I spent time reading the [official documentation](https://www.typescriptlang.org/docs/handbook/intro.html) if I wondered how something in the codebase worked. I paired with a teammate to ask questions and used the [playground tool](https://www.typescriptlang.org/play) to test how things worked.
As I moved along, I wanted to see how TypeScript worked on the frontend, too. So, I created a fun, small project I called [pretty kitties](https://github.com/mindiweik/prettykitties) using a [free cat API](https://thecatapi.com/)! I’m glad I did this because there are some really interesting nuances using React with TypeScript! I found a really helpful article on using the two together when I worked on this project, but I haven’t found it again to share. (If I do I sure will!)
Having an active codebase to contribute to offered plenty of practice! Code reviews were essential in helping me formulate a better understanding and provided different ideas on how to approach the styling and usage of the tools.
That said, I also wanted more practice outside of work. I picked up a side project with some friends, a team of 4 developers including me, and we’ve been working on codebases involving a backend API and frontend for a project I hope to share more about soon!
Working on another team showed me even greater variation of styles and practices and I’ve learned _so_ much more working on this side project and practicing my skills than if I had simply stuck to my work alone.
I’ve been keeping my eye out for more opportunities to work on other projects. One open source project has caught my attention, but I also need to figure out a good balance for myself to be able to contribute to more while also working, contributing to the side project, having a life, and writing this lovely, informative blog!
**I’ll get there soon enough!** 🤩
## experiences with a local gitlab runner: part 1
URL: https://mindiweik.com/blog/experiences-with-a-local-gitlab-runner-part-1/
Published: 2024-04-09
A recent achievement of mine involved setting up a local GitLab Runner for a side project. 👏
Creating a server to run our CI/CD pipelines was an incredible experience! I learned how the process works for tools typically provided but with the benefit of having **control long-term**.
This adventure involved working on a team of 4 developers. As we built, I wanted to bring a CI pipeline for smoother and faster development (we haven’t yet deployed, so no CD just yet). There's one less thing to worry about in code reviews if you know in advance something will break the build, doesn't follow linting rules, or doesn't pass the tests.
We’re using a [free GitLab account](https://about.gitlab.com/pricing/) for now; 400 monthly compute minutes are included, but we weren’t sure how much we would need to use each month upon implementation.
It might be overkill, but I wanted to take on the challenge. So now we use a local GitLab Runner!
- **In part 1, I’ll share the first steps of setting up a GitLab Runner.**
- [Part 2](/blog/experiences-with-a-local-gitlab-runner-part2) will discuss how I moved the GitLab Runner to a Raspberry Pi
We’ll cover:
1. 👉 Some Basic Terms
2. ✅ Three Main Steps
3. 📝 Experience Synopsis
_Let's start by building shared understanding. I've added links for further reading, but I won't go too deep!_
## 👉 some basic terms
**CI/CD**, or [Continuous Integration Continuous Delivery/Deployment](https://www.redhat.com/en/topics/devops/what-is-ci-cd#:~:text=CI%2FCD%2C%20which%20stands%20for,a%20shared%20source%20code%20repository.), is a best practice in developing software. It allows frequent automated checks when changes are ready to be considered for a merge with the overall team repository.
It’s recommended to add this processing early in the project development cycle. If you wait until your project has grown large, issues that could have been easily identified during each CI/CD check and resolved more quickly than trying to work through a big stack of issues all at once will emerge.
If you're not familiar, GitLab offers **version control** which is widely used to manage code changes and archive a revision history. This is especially handy to “roll back” changes if something goes wrong!
GitLab is a _**distributed**_ version control system that allows multiple developers on multiple computers - and even multiple geographic locations - to collaborate. With the rise of remote work, and in my own experience, this is far more popular than a _**centralized**_ version control system, which stores a repository on a single server or developer’s local machine.
_[Here’s a link to more reading about version control and GitLab.](https://about.gitlab.com/topics/version-control/)_
## ✅ three main steps
_The following outlines my process with links to GitLab’s excellent documentation and tutorials. If you’d like to try any of these, I recommend using GitLab docs, as I found them exceptionally valuable and easy to follow!_
Before starting, I needed:
- **a GitLab project** set up to create a pipeline to use on the new GitLab Runner. Thankfully, we had the frontend and backend projects already set up.
- **a maintainer or owner role** for the project. I joined an existing project with basic access, so I requested access.
- to determine the **machine(s)** to use for the GitLab Runner. I decided to start with my MacBook I use for personal projects.
**Once the project was set up and the correct permissions were confirmed, my general steps included:**
1. Create a GitLab Pipeline
2. Install and Create a GitLab Runner
3. Register the GitLab Runner for the Pipeline
### create a gitlab pipeline
The [GitLab pipeline tutorial](https://docs.gitlab.com/ee/ci/quick_start/#steps) states that Runners should be available before starting, but my process began with the pipeline itself. Setting up the CI pipelines was the original goal. I wanted to take small, iterative steps.
I built 2 working pipelines using a GitLab.com Runner first.
One of the best tools was the [CI lint tool](https://docs.gitlab.com/ee/ci/lint.html) which performs a YAML file check to identify invalid syntax and rules. It significantly helped when trying to tweak the pipelines!
This can be accessed under the **Build** option in GitLab > choose **Pipelines** > and select the **CI lint** button in the upper right corner.

Here, you can enter your YAML file contents and click on **Validate** which will show you any linting issues or invalid syntax.

### install and create a gitlab runner
After a short celebration 🎉 I moved to the next challenge: installing a local Runner.
The first step is to install GitLab Runner onto the chosen machine. I planned to keep it simple at first, using my MacBook. It’s important to remember that GitLab Runner is ultimately an application running on your device. _We’ll discuss this in the synopsis, but [here are some more details from GitLab](https://docs.gitlab.com/runner/)._
I found the [installation steps](https://docs.gitlab.com/runner/install/) and followed the [macOs](https://docs.gitlab.com/runner/install/osx.html) directions. A Runner can be installed on many operating systems and architectures, but not all of them. _We’ll talk more about this in [part 2](/blog/experiences-with-a-local-gitlab-runner-part2)._
The installation was relatively easy, and I didn’t run into too many issues.
Next, a new Runner needs to be created within GitLab to connect with your project(s). This step will also generate a token you’ll use later. This can be accessed under the **Settings** option in GitLab > choose **CI/CD** > locate **Runners** and click to Expand.

Here, choose “**New Project Runner**” and enter basic details like the tags you want to use and the name of the Runner. For instance, I used a tag in the **`.gitlab-ci.yml`** for the CI pipeline to trigger on a specific Runner: **`mindi-local`**. I opted to create project-level Runners for both the frontend and backend repos, and these steps were necessary for both projects.
Upon creation, leave the new Runner confirmation screen up. There are registration directions, but more important, your token is only visible on this screen unless you copy/paste it elsewhere.

Let’s move on to the next step.
### register the gitlab runner for the pipeline
Once installed, it’s necessary to [register the Runner](https://docs.gitlab.com/runner/register/). Here was where I found it a little tricky. There are some basic directions on the GitLab screen you have open from the step above.
The biggest challenge was navigating the CLI to access the **`config.toml`** file to look over the settings. With practice, it became easier to access and update.
_[Extra details about the `config.toml` file](https://www.devopsschool.com/blog/gitlab-tutorials-understanding-about-config-toml-example-of-config-toml/#:~:text=You%20can%20find%20the%20config,the%20path%20for%20service%20configuration%29) and a [GitLab Runner commands](https://docs.gitlab.co.jp/runner/commands/) reference._
From here, the CLI was not too difficult. I ran this to start registration:
```bash
gitlab-runner register
```
…and then followed the prompts.
- Enter the GitLab URL to use. I chose to use **`https://gitlab.com`** for simplicity.
- Enter the Runner authentication token provided on the creation screen, pictured above.
- Enter a name for the Runner.
- Enter the type of [executor](https://docs.gitlab.com/runner/executors/index.html). I chose **`shell`** for my use case in the CLI.
After successful registration, all that should be needed is:
```bash
gitlab-runner start
gitlab-runner run
```
…and your local Runner should connect and begin listening to GitLab for new pipeline jobs to run locally!
It helped that I had preemptively added matching CI pipeline tags to the **`.gitlab-ci.yml`** and the Runner created on the GitLab site. Once properly up and running, the CI pipeline ran without issue. 👍
As it was my first time, I had to do some troubleshooting, mainly for settings. Don’t be surprised if it doesn’t work perfectly the first time. However, I was able to figure everything out with a bit of research!
## 📝 experience synopsis
Generally, the process was manageable and a fun challenge!
Have I mentioned the GitLab documentation is great? 😅 Seriously, it was an excellent resource and helped me get “unstuck” more than once.
A positive of the GitLab Runner is that it’s open-source! The barrier to entry is pretty low. It has no language-specific learning requirements, and pipelines are set up by simply adding a YAML file to the project's root. With the YAML file, I set up a relatively simple CI process in one day, running scripts from the **`package.json`** file.
On the other hand, I faced a significant challenge keeping the Runner active on my MacBook. This left me with less computing power for development. Remember when I mentioned that the GitLab Runner is essentially an application on your machine? Keeping the Runner “open” meant that the application was running in the background, consistently pinging - or checking in with - GitLab to see if there were any jobs to run.
I would turn off the Runner to gain more resources, only to manually turn it back on when a PR was submitted to run the pipeline.
For obvious reasons, this manual back-and-forth wasn’t optimal. Instead, I considered how I could approach this another way. I remembered a Raspberry Pi I hadn’t used in some time. It was the perfect option for a local Runner I could keep open for the team!
_**In [part 2](/blog/experiences-with-a-local-gitlab-runner-part2), we’ll talk more about how it went moving the local GitLab Runner to a Raspberry Pi. I learned a LOT in this second part!**_
_**Looking forward to it, see you then! 👋**_
## peek behind the bootcamp curtain 🧐
URL: https://mindiweik.com/blog/peek-behind-the-bootcamp-curtain/
Published: 2024-03-19
All coding bootcamps differ. This applies to the people who participate in them, too. Every experience is different!
I completed a bootcamp after 2 years of learning on my own. I'm driven but needed more accountability and opportunities for deeper understanding or to locate _trusted_ resources. A rapid feedback loop helps to retain complex information long-term.
After months of research, I selected the best program for me, my partnership, and my career goals. I chose the part-time Hack Reactor (by Galvanize) Software Engineering Immersive*, which encompassed 9 months of nights and weekends plus a significant cost. Afterward, I was better prepared than I managed on my own!
We’ll cover my experience for someone considering this path or for curious minds!
1. 🤔 Takeaways
2. 🤯 Challenges
3. 😌 Advice
_\*The part-time program was discontinued in 2023; as of this writing, only full-time is available._
## 🤔 takeaways
I learned so much, even beyond the technology!
#### communication is extremely important.
Working in tech necessitates clear communication to solve challenging problems. Our projects involved “clients” and instructors acted as engineering managers, offering project scope navigation.
Asynchronous communication is common in a remote program. For some, this was new. Having worked remotely since early COVID, it was already normal for me.
These experiences highlight communication layers that happen in tech. The importance is discussed in newsletters like [Soft-Skilled Engineer](https://open.substack.com/pub/tsse), [Techlead Mentor](https://open.substack.com/pub/ravirajachar), or [High Growth Engineer](https://open.substack.com/pub/careercutler). Not everyone makes it.
I was surprised I didn’t get in the first time. When I applied, Hack Reactor required a technical test for acceptance. They offered free prep training, but you had to take advantage of this. Rejection pushed me when I found it wasn’t easy; I got in on the second try!
Along the way, my cohort lost several people at various stages and reasons. Some were personal, others decided the content wasn’t working, and a few opted to pick up with a later cohort. I learned recently that a later cohort lost about half of their number!
Hack Reactor gave the ability to stop or postpone at various points which could result in a full or partial refund, a do-over with a later cohort, or a complete removal without a refund. The stipulations for each were fair to me.
#### you learn quickly in a fast-paced program.
With strictly planned coursework, there’s a tight deadline. The first weeks included foundational training followed by projects with quick deadlines of 4-6 weeks. Mid-way through was a major test to confirm we gained the knowledge to finish the second half of the course.
We had to quickly pick up new tech to complete projects on time while practicing existing tools and communicating with our team and instructors. We learned to ask questions early and often. It’s not worth struggling well into a deadline!
#### projects are the best possible learning tool.
I’m certain you’ve heard this before. Tutorials and reading can only get you so far.
Touching the tech, working through confusion, and practicing in a project are the best ways to learn. I’ve taken that seriously since diving into my career and side projects.
## 🤯 challenges
No one told me bootcamp would be easy. I was well-informed that it would be difficult. We talked about it at length during our first week of onboarding. I was unprepared for it to be one of my toughest experiences.
#### investments are high.
Of course, there was a large financial investment. I chose a high-cost program because acquaintances completed it and did well over the last 5-6 years. Real results are the best recommendation!
I underestimated the time investment. The course itself involved Zoom meetings over 10 hours each week. Then another 10-15 hours were needed to finish projects, outside learning, and assignments.
The info was clear, but living it was harder. I didn't have the luxury to leave work during my course, so I also worked full-time throughout.
Weekdays for me consisted of meetings with customers, my team, and other teams. When I didn’t have meetings, my work was also on a computer. Most evenings, I took a short break before class started but I was still overwhelmed and exhausted. _I’m extremely grateful to my partner for taking on more so I could focus._
Saturdays involved a 5-hour class followed by outside coursework. The program recommended taking Sundays off, and I tried, but it was my best time for a clear mind to focus and grasp content. I often worked on Sundays as a trade-off to rest after long workdays.
#### life goes on during the course.
Whether a program takes weeks, months, or one or more years to complete, this is a valid challenge and the one I least expected.
I suddenly lost someone important to me, learned my father had lung cancer, and we learned our beloved dog had bladder cancer, and we needed to make tough decisions as caregivers.
I’m sure others had hardships, too, but this was a level I had never faced. My career and coursework were challenging, and my personal life added even more to juggle in the last half of the course.
## 😌 advice
#### you get out of it what you put into it.
No matter the program, you get reciprocal success for the effort given. If you learned the fundamentals, you were quicker to grasp the next phases. If you spent time on individual tools, you were more capable of applying them in projects.
My engineering manager wrapped up my effort neatly in my first annual review as a Software Engineer.
He was blunt with me. He wasn't thrilled when he learned my company committed to hiring a new engineer who recently completed a bootcamp. He shared that immediately, he was impressed by my ability to speak to technical challenges, the smooth onboarding for a newer engineer, and my ability to communicate.
Which leads me to my next point.
#### career changers are powerful!
These days, many people find tech after pursuing other careers. This brings a wealth of knowledge and experience from different domains and levels.
Right away, I applied my experience in US real estate to my company where we build tools and websites for North American agents. My empathy and understanding of customer needs from my time in Support and Onboarding brought new insights to our team and projects.
**If you’re a career changer**, take your experience with you! It’s truly valuable.
**If you’re a hiring manager**, your team will be more diverse if you hire folks with varied experience. Even if it’s not as directly correlated as my experience, we bring all sorts of skills to build better software!
#### job searching.
Here is the best I can give: leverage your network. For my first role, I am grateful that my company supported my efforts and moved me to engineering after completion.
I didn’t go through what many in tech have in recent years. Multiple interviews, tests, projects, problems, recruiters…I leveraged opportunity.
I worked hard for them and clarified my goals - to give myself proper credit. I talked to the leadership team early and often. Granted, I work for a small company with under 100 full-time employees; I had the ability to speak with the C-suite directly.
## conclusion
Results will vary. Honestly, I could leave it there, but I’ll wrap it up.
My bootcamp was hard and I learned a crazy amount in a relatively short time. My investments were large, but my rewards have been great thus far.
Bootcamps are not for everyone. There’s more communication involved than many believe when considering this career, but it’s an extremely important asset. Some people learn coding isn’t for them or choose to leave before finishing.
Bootcamp grads should share their career goals with their network to help them find that first role! I’ve heard it gets better once you land the first one. I’ll have to get back to you when I eventually move to my next role. 😉
## fundamentals of backend engineering course review
URL: https://mindiweik.com/blog/fundamentals-of-backend-engineering-course-review/
Published: 2024-03-12
I recently completed the [Fundamentals of Backend Engineering](https://www.udemy.com/share/107rGq3@Nbj-Z2Zckw7X9diaLNDMLP9XC8TUPF285j_NxZ11XNKQGrRHDIgTisjY2NCgn2otaQ==/) course on [Udemy](https://www.udemy.com/) and shared this accomplishment on [LinkedIn](https://www.linkedin.com/posts/mindiweik_udemy-course-completion-certificate-activity-7166921897345998848-3zha?utm_source=share&utm_medium=member_desktop)!
Let’s look at the course from a high level and find out how I think it went. 🤔
**We’ll cover:**
- 💻 Overall Content
- 🎯 Who is the Best Audience?
- 🛠️ Are the Skills Real World Applicable?
- ⏳ Time Commitment
- 💸 Value
- 🎬 Instructor
- 👉 Conclusion
## 💻 overall content
### 🎯 who is the best audience?
Any engineer can leverage knowledge of backend engineering. So, pretty much any active developer could take this course!
Admittedly, a backend engineer will likely get more from the course as it is geared toward this group. But I can see some situations, too, where a frontend engineer can benefit from a basic understanding of these concepts.
That said, this isn’t as ideal for a brand-new engineer with little experience. I’m not saying, “Don’t take it,” if you fall into that category, but I would recommend some self-guided digging into how the internet works and getting a nice base level of knowledge before taking the course. Nothing too thorough; he covers some basics. Having a point of reference for some terms would be useful.
### 🛠️ are the skills real world applicable?
A resounding yes!
While working on a current project to build a greenfield API, I found that learning more about polling and long polling, TLS, TCP, WebSockets, and Stateful vs. Stateless were all timely and helpful.
These concepts directly aided my understanding of the full scope of the project and how certain services were currently interacting together and planned future interactions.
It was helpful for me, and I could see any of these areas being helpful for another developer in the right project or scenario.
## ⏳ time commitment
The course holds 55 lectures with 16 hours of content.
It’s one of the biggest Udemy courses I’ve taken to completion! I spent about 4.5 months working on this course.
Often, it feels good to finish quickly. But I took my time watching a lecture every few days. I did this intentionally to absorb the content fully; I was committed to forming a solid foundation for the future.
## 💸 value
The currently listed cost of this course is $94.99 USD.
Udemy often runs sales. I purchased this course through one of the bigger sales at $15.70 USD in October 2023.
If you are lucky enough for your company to have a deal with Udemy to provide you with free or reduced-cost course access, that’s even better!
Considering all of the in-depth content, I find this course to be very affordable. Of course, it’s even more affordable when purchased through a Udemy sale. Having been through the content, I feel the full price would be worth the investment.
## 🎬 instructor
[Hussein Nasser](https://www.udemy.com/course/fundamentals-of-backend-communications-and-protocols/?couponCode=KEEPLEARNING#instructor-1) is an Engineer with about 25 years of experience, which is apparent during his lectures. As he dove into a lecture or a concept, his excitement encouraged my desire to learn more!
At times, the excitement made the concepts difficult to understand. He does a pretty good job of keeping the jargon low-key or taking the time to explain what he’s talking about, but there were a few areas where I had to re-listen/rewatch or do some extra digging to get a better understanding.
This isn’t necessarily a bad thing! It also encourages self-discovery and promotes curiosity to some degree.
The lectures are a collection of videos; some are from other courses by Hussein. In some ways, I admire his ability to create videos that can be used to help scale and bring more understanding to multiple courses.
Finally, if interested, there is a repo you can reference alongside the code examples. It was cool to see a small chat service and how that might work locally, for example!
## 👉 conclusion
**All things considered, this course was useful, engaging, and valuable to me. I highly recommend it to developers with a base level of knowledge who want to learn more about how things work “under the hood!”**
Any developer can benefit from this course, but I recommend forming a solid base of the internet and common terms to grasp the concepts better.
With about 16 hours of content, it’s best absorbed in small doses over time!
This course has so much usable content. I used it more than once in my day-to-day role. As mentioned, I feel the content fulfills the value offered even at full price.
Hussein Nasser brings a wealth of knowledge and experience. Come prepared for energetic and informative lectures with plenty of visual aids!
### 🥸 proof of completion, just for fun:

**Mindi’s Udemy course completion certificate**
### 👉 bonus:
I was looking for new Podcasts to listen to during the course. I poked around a bit and found that Hussein Nasser has one if you want to listen! [Backend Engineering](https://www.husseinnasser.com/p/podcast.html)
## how to start working with ai
URL: https://mindiweik.com/blog/how-to-start-working-with-ai/
Published: 2024-03-05
I engage in several Slack communities in varying spaces. I like to keep a pulse on what’s happening in the industry and within certain spheres. In a global “women in tech” community, a question arose that I enjoyed and inspired this post!
**In this post, we’ll cover:**
1. ❓ The Question
2. 📌 My Response
3. 🤖 Useful Tips
## ❓ the question
Let’s start by understanding what prompted the question and where her headspace was.
> "So my boss got me a subscription to the jet brains AI assistant and I want to make a good faith effort to use it effectively ([that new yorker piece](https://www.newyorker.com/magazine/2023/11/20/a-coder-considers-the-waning-days-of-the-craft) I saw people discussing in another channel kinda got in my head.) Any tips to get me started???"
>
> [Sarah] - (New Yorker link added for reference)
The first aspect is that she had an AI subscription to try. And she wants to attempt to use it genuinely.
On top of that, she has hesitations after seeing a conversation about an article that seemed to waffle back and forth on whether AI is good or bad in the context of coding.
**Substack’s AI image generator created a “person programming on a computer” image in a “painting” style.**
Related to the article and discussion, she shared:
> "…many found it pretty whiney, which is fair. But I was also intrigued by the picture it paints of AI as a tool for making human coders faster and more effective, as opposed to automation."
>
> [Sarah]
I couldn’t agree more with her interpretation; this mindset forms my thinking about AI! Let’s look at how I responded and some tips I offered.
## 📌 my response
My response has been edited to fix spelling and punctuation, add space and links to bring meaning, and be more visually appealing than a big text block. The core of the response is still present.
I'm unfamiliar with Jet Brains, and I'm unsure if this will be helpful, but I can share my experiences! I have access to [GitHub Copilot](https://github.com/features/copilot) in my IDE, and I use both [GitHub Copilot Chat](https://docs.github.com/en/copilot/github-copilot-chat/about-github-copilot-chat) and [ChatGPT](https://openai.com/blog/chatgpt) sometimes. To be clear, they're helpful, but most definitely not always right!
For work, it has helped me improve my speed with writing tests in the IDE. It's so much faster and just needs a little tweaking here and there so I can improve coverage while also spending more time on the "fun" and challenging parts.
I also make sure to turn Copilot off when I'm trying to learn something new so it doesn't write things for me so I can learn better. It's a simple toggle, so that is helpful.
Having access to AI has also helped me more quickly understand a large codebase in a language I'm not as familiar with. I can ask what a particular function is doing and it can read through the files. File reading is limited to GitHub Copilot and Chat, but it's pretty cool! Or if there is a syntax I'm not familiar with I'll ask for a synopsis of how it works in a code block.
I use the chatbots to help me craft and tweak regex patterns, asking about possible alternatives to a solution I'm not considering, and when working more on the front end I'll ask how to achieve a certain look with the given tools.
Personally, I also love to write some basic ideas down for something I want to write up (recently, I did this for a peer recommendation) and then have ChatGPT rewrite it more eloquently. Then I go back and tweak it again afterward so it sounds more like what I would actually say. Sometimes we go back and forth a few times 😅
My point is really that I agree that AI will be a force to make us faster, more efficient, and more accurate and there are so many creative ways to use it! I think if you want to practice, you could start by asking the chatbot when you want to Google something and then see where it leads. Honestly, there are times when it doesn't work out, and that's okay! It doesn't waste too much time to give it a shot, and you know where to look if it doesn't pan out.
## 🤖 useful tips
### from my response, I would pull the following tips:
- Ask a chatbot first when you want to Google something. You can confirm it afterward. Even if it’s incorrect, you generally don’t waste much time.
- AI is helpful when coding, writing, and more, but you should _always_ double-check the work!
- You may need to ask a few times to get your desired response or request adjustments.
- Use AI for more “boring” or repeatable tasks as much as possible.
- When trying to learn something new, either remove AI assistance (working to master) or leverage it to build foundational knowledge (working to understand) and ask it to “explain this to me like I’m X years old” in the latter scenario.
And, finally, this wouldn’t be a good article about AI without asking AI for input! As usual, there are some good nuggets here, but some suggestions are odd, even after going back and forth a few times.
## chatgpt’s 10 tips to leverage ai in your day-to-day
"These actionable tips provide a more hands-on approach to incorporating AI into daily activities, especially for tech professionals.”
1. **Start with Small AI Projects:** Begin with manageable AI projects to build expertise incrementally.
2. **Utilize AI-Powered Tools:** Integrate AI-driven tools for project management, data analysis, and code optimization.
3. **Experiment with Open Source AI Frameworks:** Engage with open-source AI frameworks like TensorFlow or PyTorch for hands-on learning.
4. **Verify and Interpret AI Results:** Implement rigorous testing and validation procedures to ensure AI model accuracy.
5. **Regularly Update AI Models:** Establish a schedule for updating AI models to keep them effective and secure.
6. **Integrate AI in Content Consumption:** Explore AI-driven content curation tools to stay updated on industry news.
7. **Participate in AI Communities:** Engage with AI communities, forums, and conferences for networking and knowledge sharing.
8. **Explore AI in Health and Fitness:** Develop AI applications for health monitoring and personalized fitness routines.
9. **Implement AI in Home Automation:** Design AI systems for smart home automation, optimizing energy usage and security.
10. **Educate Others on AI Benefits:** Share insights on AI benefits and dispel myths to promote informed discussions.
### 🤩 a final tip:
Say “thank you” to your AI assistant(s)! It feels good - at least to me - and it covers your backside if AI gains sentience one day. I’m only half-joking. 😉
_**Please note that I acknowledge AI has flaws, most importantly built-in biases.**_ I do not want to encourage anyone to participate in AI who might be uncomfortable. These are simply tips and recommendations on engaging with AI tools if interested!
## clean code by robert c. martin
URL: https://mindiweik.com/blog/clean-code-by-robert-c-martin/
Published: 2024-02-20
## _clean code_ is a **classic**.
I am not the first to summarize; I won’t be the last! Thank you to my partner for gifting this book to me when I started my career change. 🙏 After I read _Clean Code: A Handbook of Agile Software Craftsmanship,_ it was requested I share insights with my team. Any chance to share knowledge is a win! I share the details included in my presentation here, focusing on the most widely applicable content.
**Quick notes:**
- ❌ _The author uses Java-specific references frequently. I’ve removed these because they aren’t relevant to myself or my team._
- 👀 _There’s an immense level of detail. Please feel free to jump around as you see fit!_
**Here, we’ll cover:**
- 🎬 The Presentation
- 💡 My 3 Big Takeaways
- 💻 What is clean code?
- 🏗️ Foundations: names, functions, comments, and formatting
- 🚀 Put it into Practice
- 🥸 Code Smells
- 🤔 Questions to consider
- 🫶 Bonus Recommendation
## 🎬 the presentation
The conversation that was sparked pleasantly surprised me when I presented to the team in the Western hemisphere. Some insights:
- Although one teammate read this several years ago, it’s still pertinent for them in their current DevOps role!
- Our team is responsible for taking this seriously and taking the time to do this in our daily work.
- We all need to practice this, regardless of our current experience level.
[Here is a link to the slides!](https://www.canva.com/design/DAF5m0phHYY/vB6CGXGNUejdrCYUo4uTKw/edit?utm_content=DAF5m0phHYY&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton) And here’s a recording of the presentation (~15 minutes):
## 💡 my 3 big takeaways
1. **Writing code is a process**. Start with a draft, refine, and repeat. It takes practice.
2. What ultimately matters is that your team **agrees on standards and sticks to them**.
3. It is _**everyone’s responsibility**_ to keep a codebase clean!
## 💻 what is clean code?
For me, clean code is beautiful and embodies _**discipline**_. It is concise and clear to any reader. Business needs and team members will inevitably change! So, it’s important to hone the craft to solve a problem with meaningful code for others to understand.
I especially like the application of the referenced “broken windows” metaphor:
> "A building with broken windows looks like nobody cares about it. So, other people stop caring. They allow more windows to become broken. Eventually, they actively break them. They despoil the facade with graffiti and allow garbage to collect. One broken window starts the process toward decay."
>
> Dave Thomas & Andy Hunt
Several other interpretations of “clean code“ were provided by others in the industry. Here are some of the meaningful interpretations I extracted:
- elegant/pleasing
- efficient
- readable
- does one thing well
- easy for others to enhance
- well-tested
- minimal/simple and orderly, well-cared-for
- no duplication
- no surprises - **it returns exactly what you expect**
Without developer discipline, building new features slows over time. As the mess grows, so does the time needed for the codebase, especially for new team members! They must wade through it to make an impact.
Clean code sometimes requires pushback on timelines (within reason) for higher-quality output. Other team members (i.e., managers or product) rely on our honest estimations. Sure, we could get it working as a bare minimum, but we also need time to care for the code, keeping the product/service smooth and efficient. This should be acknowledged and considered. Why? Because even when we say we’ll return to it, historically, we won’t.
Tidiness takes a team effort to clean up - and maintain - the codebase. Martin recommends the Boy Scout rule when working: “Leave the campground better than you found it.” Care about your craft!

## 🏗️ foundations: names, functions, comments, and formatting
What does this actually consist of? Let’s cover major areas where pain points arise.
## names
What’s in a name anyway? Well, a lot of meaning, actually!
### **variable naming tips:**
**USE**
- Pronounceable - “Humans are good at words…If you can’t pronounce it, you can’t discuss it without sounding like an idiot.”
- Searchable - For example, static numbers or hard-to-read code like a regex pattern for a valid phone number `^\+[1-9]\d{1,14}$` could have a named variable of `validE164FormatRegex` instead.
- Intention-revealing
- Meaningful context - Instead of a vague `id` variable, we can be more specific with something like `serviceXCustomerID` to make it clear what we’re working with.
**AVOID**
- Disinformation - If a variable is a number, it should not be named `moneyString`!
- Abbreviations
- Unnecessarily long names or similar spelling
- Characters that look alike - Common offenders are lowercase `L` and uppercase `i` or number zero and uppercase `o`.
- Number series naming - `a1` and `a2` mean nothing to the reader in a program.
- Avoid programming terms when a variable is not used for that use
- Noise words - These include words like “a” or “the” if it doesn’t add useful meaning.
- Encodings and mental mapping - This adds unnecessary cognitive load to decipher meaning.
### **class naming tips:**
**USE**
- Verb/verb phrases for methods - Methods take action!
- One word per concept - Try to retain the same lexicon and be consistent.
- Solution domain - Use commonly known terms that programmers will understand.
- Problem domain - When there is no “programmer-ese,” rely on the problem space to describe the class.
- Meaningful context
**AVOID**
- Noun phrase names
- Too much context
- Cleverness, slang, puns - Too many teams are globally distributed, and these won’t have the same meaning in all countries or even within different teams in the same country!
## functions
Functions are the heart of any program. They should have consistent blocks and indentation for an easily followed nested structure.
- Functions should also be small. How small?
> "The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that."
- Functions should only do one thing.
- The output should be exactly what you expect.
- If you can, extract another function.
- Functions should have one level of abstraction.
- The function should only be able to access one abstraction level below itself.
- Functions should be able to be read from top to bottom.
- Well-written code reads like a narrative as if in a set of “to” statements.
- Avoid switch statements when possible.
- Switch statements are hard to keep small, but they are sometimes useful. When using switch statements, Martin recommends burying it as low-level as possible, preferably in a class (creating polymorphic objects), to ensure it is not repeated and hide it from the rest of the code.
- Use descriptive names for functions.
- Verbs or verb phrases are great here. Let’s name functions by the action they perform! Don’t be afraid of long names if it adds clarity.
- Keep function arguments to a minimum.
- Think carefully and use a data structure if you need more than 2 arguments.
- Verbs or keywords can make the intended use of arguments more obvious.
- Functions should have obvious side effects.
- If a side effect is intended, make that clear in the name. Avoid it if possible.
- Functions should either do something or answer something, but not both.
- Prefer exceptions to returning error codes.
- Martin recommends exceptions to handle errors separately.
- Don’t repeat yourself (DRY).
## comments
Everyone has their own view on this. Regardless, note point 2 in my main takeaways. If your team agrees on a pattern, adopt the pattern. Ultimately, the best advice I’ve received is that a comment should explain _**why**_ you made a decision (like business needs) and not _**what**_ your code is doing.
- Delete dead code.
- We have version control for a reason!
- Good comment examples:
- legal comments
- informative, truly useful comments
- explanation of intent
- amplify section importance
- add clarification
- warning of consequences
- TODO comments (within reason)
- Bad comment examples (most comments):
- mumbling, personal journal/log comments
- redundant comments
- misleading comments (not precise enough to be accurate)
- mandated comments or noisy comments
- don’t use a comment when you can use a function or a variable!
- position markers
- closing brace comments
- attributions and bylines
- commented out code
- HTML comments
- nonlocal info
- too much info
- not obvious connections
- function headers
## formatting
Why is this a foundation? It’s too important to ignore, and so many linting tools exist. There’s no excuse. Because there are many tools, I won’t go into excessive detail!
- Vertical formatting (200-500 lines per file recommended)
- smaller files reduce scrolling
- space between concepts keeps them readable
- order concepts from high to low priority
- Horizontal formatting (20-60 characters per line, 45 on average recommended)
- keep lines short
- horizontal spacing can add clarity and understanding
- indentation shows the hierarchy
- avoid silent/floating semi-colons!
Again, it doesn’t matter what you do for formatting as long as everyone agrees and follows it on the team!
We’ve covered the foundational concepts! But, as Martin says, just because we might be able to recognize “dirty” code or “code smells,” we must also put it into practice!
> "So too being able to recognize clean code from dirty code does not mean that we know how to write clean code!"
>
> Chapter 1, “The Art of Clean Code?”
## 🚀 put it into practice
To put some of the following chapters into practice, I’ve pulled some of the more interesting or applicable concepts and suggestions!
- **Abstract and hide your data:**
- Reduce data manipulation and exposure where it doesn’t belong.
- Data structures expose data and don’t have meaningful functions.
- Objects hide data behind abstractions and expose functions to operate on the data.
- **Error handling:**
- Martin recommends exceptions over errors to keep the calling code cleaner and extract error-handling logic.
- Provide context with your exceptions for more informative error messages.
- Wrap third-party APIs to best handle errors from the source.
- Avoid passing or returning null. Sometimes, an API may return null, and you cannot avoid it.
- **Boundaries for Third-Party Tools:**
- Wrap an implementation around third-party code to control what is used and reduce affected code when a change you can't control occurs.
- Read the docs, test and explore a tool, and build “learning tests” (super powerful to identify API changes early) to get to know the API well!
- Define the interface you _want_ when you face the unknown. This guides design decisions. It’s better to depend on the code you can control!
- **Unit Tests:**
- Tests are just as important to keep clean! Write small, readable tests.
- Tests reduce fear of maintenance, refactors, or improvements.
- Test code doesn’t need to be as efficient as production code.
- Focus on one concept per test. Ideally, use only one assertion per test - or minimal - for easier debugging.
- Clean tests have 5 FIRST rules:
- FAST - should run quickly, so you run them often/fix them ASAP
- INDEPENDENT - should not depend on each other, diagnosis is difficult
- REPEATABLE - able to happen in any environment, reduce failure excuses
- SELF-VALIDATING - boolean output, pass or fail
- TIMELY - write in a timely fashion _just before_ the production code
- The three laws of TDD (Test-Driven Development) from the book:
1. You may not write production code until you have written a failing unit test.
2. You may not write more of a unit test than is sufficient to fail, and not compiling is failing.
3. You may not write more production code than is sufficient to pass the currently failing test.
- **Classes:**
- Classes should be small! “The first rule of ~~functions~~ classes is that they should be small. The second rule of ~~functions~~ classes is that _they should be smaller than that_.”
- Avoid a “god class” that tries to do all things.
- Follow the Single Responsibility Principle.
- Organize classes in the common standard: list variables, private instance variables, then easily read from top to bottom with important items at the top.
- Organize classes to reduce change in the case of adding functionality later and isolate the class as much as possible from external change.
- **Systems:**
- For systems, consider an example from the book:
- A hotel is _**built**_ by construction and engineering teams.
- A hotel is _**used**_ by regular people on a vacation or business trip.
- These functions are entirely independent and build/use should be the same in systems, too! E.g. Separate tests from the compilation.
- Separate the construction of a system from the usage implementation.
- Dependencies of “main” should direct _**away**_ from “main.”
- The Single Responsibility Principle or Inversion of Control moves responsibilities from an object to others dedicated to the purpose.
- Meaning an object doesn’t instantiate dependencies itself.
- Implement only what’s needed today; refactor and scale over time (incremental agility).
- Evolve from simple to sophisticated over time and with more resources!
- Optimize decision-making with modularity and separation of concerns; no one person can make decisions.
- “We often forget that it is also best to _postpone decisions until the last possible moment._”
- Waiting allows for informed decisions.
- Domain-specific language helps code read like structured prose a domain expert might write. This reduces incorrect translations!
- **Final thoughts and reminders:**
- Run all the tests! They should be easy.
- Refactor! It should be incremental (write, pause, reflect, write).
- Write dirty code, then clean it up. 🧹
- Eliminate duplication and ensure clear expressiveness for others to read.
- Choose good names, keep functions and classes small (but not too small), and use standard nomenclature and programming patterns.
- Practice makes perfect!
> "Of course bad code can be cleaned up. But it’s very expensive. As code rots, the modules insinuate themselves into each other, creating lots of hidden and tangled dependencies."
## 🥸 code smells
Below is a table of common issues provided by Martin (excluding Java-related items).
You may need to scroll to see all the goodies!
| Comments | Environment | Functions | Names | Tests | General 1 | General 2 | General 3 |
| ------------------------- | --------------------------------- | ------------------ | ---------------------------------------------------- | --------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------- |
| inappropriate information | build requires more than one step | too many arguments | choose descriptive names | insufficient tests | multiple languages in one source file | artificial coupling | be precise |
| obsolete comment | tests require more than one step | output arguments | choose names at the appropriate level of abstraction | use a coverage tool! | obvious behavior is unimplemented | feature envy | replace magic numbers with named constants |
| redundant comment | | flag arguments | use standard nomenclature where possible | don’t skip trivial tests | incorrect behavior at the boundaries | selector arguments (bool) | structure over convention |
| poorly written comment | | dead function | unambiguous names | an ignored test is a question about ambiguity | overridden safeties | obscured intent | encapsulate conditionals |
| commented-out code | | | use long names for long scopes | test boundary conditions | duplication | misplaced responsibility | avoid negative conditionals |
| | | | avoid encodings | exhaustively test near bugs | code at wrong level of abstraction | inappropriate static (referring to a method, prefer non-static methods) | functions should do one thing |
| | | | names should describe side-effects | patterns of failure are revealing | base classes depending on their derivatives | use explanatory variables | hidden temporal couplings (don’t hide it when it's necessary) |
| | | | | test coverage patterns can be revealing | too much information - hide info, keep it tight and small | function names should say what they do | don’t be arbitrary |
| | | | | tests should be fast | dead code | understand the algorithm | encapsulate boundary conditions |
| | | | | | vertical separation | make logical dependencies physical | functions should descend only one level of abstraction |
| | | | | | inconsistency | prefer polymorphism to if/else or switch/case | keep configurable data at high levels |
| | | | | | clutter | follow standard conventions | avoid transitive navigation |
**That’s a wrap!**
## 🤔 questions to consider:
1. Have you read _Clean Code?_ Did you have any different insights or any I missed?
2. What was your favorite or least favorite part?
3. Do you agree/disagree with anything “Uncle” Bob Martin shared that I have listed?
4. Have you - or are you - putting anything into practice in your daily work?
5. Is this most relevant to new engineers, or is it also useful for seniors or managers?
## 🫶 bonus recommendation
I recently began listening to the [Working Code Podcast](https://workingcode.dev/) (from the 2020 start…it’s how I listen to all podcasts). The hosts reviewed _Clean Code_ in episodes [#22](https://workingcode.dev/episodes/022-book-club-1-clean-code-by-uncle-bob-martin-pt1/) and [#23](https://workingcode.dev/episodes/023-book-club-1-clean-code-by-uncle-bob-martin-pt2/).
Thus far, I highly recommend the podcast! The real banter is great for anyone who works remotely to feel like they can enjoy technical conversations anytime!
## mongoose subdocuments & discriminators
URL: https://mindiweik.com/blog/mongoose-subdocuments-and-discriminators/
Published: 2024-02-07
In a “greenfield” project where I contribute, we use MongoDB, mainly for it's _**future flexibility**_. This brand-new project contains a lot of unknowns! I’ll outline the problem we're solving.
**Here, I assume you are familiar with databases and general JavaScript coding concepts.** I will not cover how to connect to and use Mongoose alongside MongoDB outside of the Subdocuments and Discriminators options defined below. If you need an introduction, these references from freeCodeCamp are both excellent: [Mongoose 101](https://www.freecodecamp.org/news/mongoose101/) or [Introduction to Mongoose for MongoDB](https://www.freecodecamp.org/news/introduction-to-mongoose-for-mongodb-d2a7aa593c57/).
Our API proxy service will add a safety layer between the platform and a third-party vendor. Currently, we reach out directly to the vendor, causing multiple dependencies and a web of complicated actions which will need to be detangled and simplified.
Another long-term goal is to decouple our platform, allowing more vendor flexibility in the future. You never know when something will change with an API you don’t control. 😉
## why mongodb - or why a nosql database - you might ask?
Although there is a level of flexibility you can build and connect with relational databases and foreign keys, NoSQL databases are known for their flexible data structure capabilities. Ever-growing SQL tables leads to complicated queries and multiple join tables to access scattered data. Those queries can become expensive.
I mentioned the possibility of future third-party vendor(s). Preliminary research highlighted that the data across other possible vendors is just varied enough to need schema variation. Another win for MongoDB is that it can store multiple shapes of related data in the same collection! Don’t worry, we’ll cover this in more detail.
> “Generally, in MongoDB, data that is accessed together should be stored together.”
>
> Jesse Hall, MongoDB Developer [Article](https://www.mongodb.com/developer/languages/javascript/getting-started-with-mongodb-and-mongoose/)
…and that’s the perfect segway to the crux of this topic.
### in this post, we’ll cover:
- ❓ What is Mongoose?
- 📒 Mongoose Documents
- 📑 Mongoose Subdocuments
- 🙈🙉🙊 Mongoose Discriminators
- 🛠️ When and how to use these tools
## ❓ what is mongoose?
To more easily work with MongoDB, developers frequently use Mongoose. It’s not the only tool, and a tool is not required to use MongoDB, but tools provide structure and fluidity in my experience.
Mongoose is a popular third-party JavaScript library for Node.js. It helps to model, validate, and manipulate data along with plenty of other interesting capabilities. Mongoose is an ODM, or Object Data Modeling, library.
Mongoose provides more structure to developer interactions with MongoDB. The schema allows you to define the shape of your data and its expected types as well as additional options like default values, designated uniqueness, or indexing for example.
The model, on the other hand, applies your schema structure to each of the MongoDB documents. Models are then used for “CRUD” database actions on the records: creating, reading, updating, and deleting. View the [full list of Mongoose queries](https://mongoosejs.com/docs/queries.html) available.
To ensure our mental model is aligned, here’s an example of a base schema in JavaScript we will use to define a model we can work with:
```js
import mongoose from 'mongoose';
const { Schema } = mongoose;
const foodBaseSchema = new Schema({
foodName: String,
foodColor: String
});
const foodBaseModel = mongoose.model('Food', foodBaseSchema);
```
## 📒 mongoose documents
A Mongoose document is a mapping of a model. Your model should match the pre-defined schema(s). The Model class is a subclass of the Document class in the Mongoose [implementation](https://mongoosejs.com/docs/documents.html#:~:text=Document%20and%20Model%20are%20distinct%20classes%20in%20Mongoose.%20The%20Model%20class%20is%20a%20subclass%20of%20the%20Document%20class.%20When%20you%20use%20the%20Model%20constructor%2C%20you%20create%20a%20new%20document.). When you use a Mongoose query, you are interacting with the Mongoose Document.
Let’s build on the above example as we go. Here’s the basic implementation of creating a Document with our above base model:
```js
const foodDocument = new foodBaseModel();
/** Below is borrowed from the Mongoose documentation to
aid in understanding how a Model is a subclass of a Document. **/
foodDocument instanceof foodBaseModel; // true
foodDocument instanceof mongoose.Model; // true
foodDocument instanceof mongoose.Document; // true
```
## 📑 mongoose subdocuments
A Subdocument represents a Document embedded inside another Document. In other words, a Subdocument can also be defined as a schema within another schema.
As we work with Subdocuments, you might notice that they look pretty similar to Documents. They are! The main difference is a Subdocument will be added to a “top-level” or “parent” schema and can only be accessed and interacted with alongside the parent schema. Additionally, if you use any of the built-in Mongoose middleware or validation options on the Subdocument, this will be performed _before_ the Document to ensure everything is in order before proceeding at the top-level.
Meaning, Subdocuments are not stored in a separate table and accessed with a join table like when using a relational database. Subdocuments cannot exist without their parent , MongoDB stores all of this data in a single Document.
_(That said, MongoDB does have the [capability of a “join” view](https://www.mongodb.com/docs/manual/core/views/join-collections-with-view/) if you’re interested.)_
Okay, great, now that we have a pretty solid understanding of Subdocuments, let’s expand the `foodBaseModel` with a couple of ways to add Subdocuments!
```js
/** Let's define a schema for the ways we can cook said food item. **/
const cookSchema = new Schema({ type: String });
/** And we'll add a schema with data about how this item is grown. **/
const growSchema = new Schema({ detail: String });
const foodBaseModel = new Schema({
foodName: String,
foodColor: String,
// Array of Subdocuments to list the cooking types we could use
cook: [cookSchema],
// Single nested Subdocument to allow us to define a growth schema
grow: growSchema
});
```
#### subdocuments and nested paths are different
Before we move on, we should cover a common point of confusion. A nested path is not the same as a Subdocument, though they do look quite similar.
Try to keep an eye out for the subtle differences of this nested path example:
```js
const nestedFoodSchema = new Schema({
foodName: String,
foodColor: String,
flavor: {
delicious: Boolean,
kidFriendly: Boolean
}
});
const nestedFoodModel = mongoose.model('Nested', nestedFoodSchema);
```
Although this might look close to what we did above, and they may look similar via MongoDB, Mongoose treats these differently.
1. A nested path like the above must be defined upon Document instantiation to be valid whereas in our earlier Subdocument example, we can set the `cook` field to `undefined` to start. We can then more easily alter the `cook` field when ready!
2. Subdocuments are nested Documents if you recall. Because of this, Mongoose gives each Subdocument an `_id` Document identifier making it searchable as a Subdocument or within it’s parent Document.
3. Although you can certainly use JavaScript methods on a subdocument or nested path object, nested paths do not allow you to take advantage of the built-in Mongoose methods to interact directly with a Document’s list of Subdocuments like our `cook` field.
If you need more clarification, I recommend referring to the Subdocument [documentation](https://mongoosejs.com/docs/subdocs.html).
## 🙈🙉🙊 mongoose discriminators
Discriminators essentially allow you to create schemas with varying object models to store within the same collection. This is an excellent option if you have a similar underlying schema structure, but you need slight differences.
When setting up a schema, an options object can be appended at the end as another parameter. In the case of Discriminators, a `discriminatorKey` in the options object is used with a value to define the Discriminator in the Documents. This key becomes searchable using the `__t` string path.
Once a Document is created with a Discriminator key, this key is not typically able to be updated by most methods. Though there are some update methods that use the `overwriteDiscriminatorKey` option to override this.
One other cool feature is that you can apply Discriminators to Subdocuments, too! Think about all the possibilities and flexibility your schema can have. 🤔
Let’s finish our example. First, we’ll revisit the base schema to add the `discriminatorKey` option:
```js
/** Subdocument Schemas **/
const cookSchema = new Schema({ type: String });
const growSchema = new Schema({ detail: String });
const foodBaseModel = new Schema({
foodName: String,
foodColor: String,
cook: [cookSchema],
grow: growSchema
}, {
discriminatorKey: 'foodType'
});
const foodDocument = new foodBaseModel();
```
Then, we’ll create our separate schemas to add on top of our baseSchema with the `discriminatorKey`:
```js
const vegetableModel = foodDocument.discriminator(
'vegetable',
new Schema({
isOrganic: Boolean,
colorVariations: [String]
}, {
discriminatorKey: 'foodType'
});
const grainModel = foodDocument.discriminator(
'grain',
new Schema({
isFresh: Boolean,
styleOptions: [String]
}, {
discriminatorKey: 'foodType'
});
```
And finally we create the new Documents (including the Subdocuments) for each Discriminator type:
```js
const carrot = new vegetableModel({
foodName: 'carrot',
foodColor: 'orange',
cook: [
{ type: 'roast' },
{ type: 'bake' },
{ type: 'saute' }
],
grow: { detail: 'root' },
isOrganic: true,
colorVariations: [
'purple',
'white',
'red'
]
});
carrot.save();
// We now have a carrot Document!
const bread = new grainModel({
foodName: 'bread',
foodColor: 'brown',
cook: [
{ type: 'bake' },
{ type: 'fry' }
],
grow: { detail: 'cooked dough with a flour base' },
isFresh: true,
styleOptions: [
'croissant',
'italian',
'sourdough'
]
});
bread.save();
// We now have a bread Document!
```
## 🛠️ when and how to use these tools
Whether you use these options in a Mongoose project has the same answer heard frequently within the world of software engineering: _“It depends.”_
Should all Documents take advantage of Subdocuments if they have an associative object structure? No. Subdocuments are most useful for a specific schema to use and enforce for a nested object, the nested object should be searchable, or the option to leave the nested object structure off of the Document until a later time without having to define it up front could be used strategically.
And Discriminators? Yup - they’re not for every situation! I find Discriminators are best for unique situations. It’s not common to need slight variants on schemas. But, as I mentioned earlier, our case of slightly different data shapes for third-party vendor data is a great scenario for using the Discriminator option in Mongoose.
Personally, I’ve found that Mongoose has made development with MongoDB easy and enjoyable. The library is well-documented and both MongoDB and Mongoose have a great community with plenty of examples to draw from.
I hope you found the usage of Subdocuments and Discriminators by this non-relational database as interesting as I did!
**Sources:**
- [MongoDB Article: Getting Started with MongoDB and Mongoose](https://www.mongodb.com/developer/languages/javascript/getting-started-with-mongodb-and-mongoose/)
- Mongoose Documentation: [Documents](https://mongoosejs.com/docs/documents.html) | [Subdocuments](https://mongoosejs.com/docs/subdocs.html) | [Discriminators](https://mongoosejs.com/docs/discriminators.html)
- [Introduction to Mongoose for MongoDB](https://www.freecodecamp.org/news/introduction-to-mongoose-for-mongodb-d2a7aa593c57/) and [Mongoose 101](https://www.freecodecamp.org/news/mongoose101/) from freeCodeCamp
- BONUS Recommendation: [MongoDB Podcast](https://podcasts.mongodb.com/public/115/The-MongoDB-Podcast-b02cf624)
## failing fast
URL: https://mindiweik.com/blog/failing-fast/
Published: 2024-01-24
## start with failure - speed up success
As I began my endeavor to learn more in public, it felt symbolic to start with failing fast. I first heard this when [Dr. Emilyn Dale](https://www.linkedin.com/in/emilyndale/) talked about this at a [Women Impact Tech](https://womenimpacttech.com/) panel in Denver, CO, last year; it struck a chord for me. Little did I know I had already embraced it!
“Failing fast” as a term seems widely credited to author John C. Maxwell. The quote from his book _[Failing Forward](https://www.goodreads.com/work/quotes/614412)_:
> "Fail early, fail often, but always fail forward."
>
> John C. Maxwell
That said, this concept is a common Agile practice, better known as “iterations,” or attempts with adjustments. Experimentation and low-risk failures are encouraged for quicker, quality results.
This approach leads to incremental progress over time. It also encourages faster peer feedback and a higher standard of team output.
Although this process leads to innovation, failing fast doesn't work in every situation. The best is when experimentation is safe and warranted, like trying out a new idea or product.
If there is a potential for high costs or negative results you can’t easily reverse, it may not be best to try failing fast. In these cases, the costs could outweigh any benefits. Consider consequences before making a decision!
Failing fast as a practice was initially hard for me to adopt, but the value has been tremendous.
Here are a couple of examples:
1. Before Software Engineering, I was an Onboarding Manager. I was tasked to automate our manual processes to allow us to tailor our customer experiences better. This was no easy feat.
It took multiple iterations to be smooth and efficient! Each adjustment got us closer to better performance. I admit I had unpleasant calls with customers when an unexpected automated email went out, for example, but the benefits were greater: we reduced launch time by 15 percent (appreciated by customers and our team) and improved customer satisfaction by 25 percent in under a year!
2. Currently, my team is building a proxy service to decouple our platform from a third-party vendor to add a safety layer to the platform and allow us to accommodate adding a new vendor. It’s a greenfield project; we try approaches and pivot if we find a better solution.
Recently, we found an approach using a single endpoint over multiple endpoints with similar formats. After identifying this solution, we cut approximately 30% of this newer codebase, but the result is cleaner and easier for the platform to consume. It also _appears_ more efficient; I haven’t tested it.
I recommend fostering a company culture of experimentation and innovation, but it doesn’t work for all businesses. As an individual contributor, embracing this concept when possible is good to help you make quick progress!
## **ideas to try in your own projects or at work:**
- Focus on one concept at a time. Iterations should be small.
- After you read about ideas or approaches, try them!
- Have a project idea in mind? Mock it up in a simple way.
- Document results; back up your decisions with data!
- 🤩 A great tip for cultures that don’t foster failure.
- Consider if the experiment can be easily reversed.
- 🥸 If so, go for it! If not, think twice and be careful.
> "An important goal of the fail fast philosophy is to avoid the sunk cost effect, which is the tendency to continue investing in something that clearly isn't working because it's human nature for people to want to avoid failure."
>
> [TechTarget.com post](https://www.techtarget.com/whatis/definition/fail-fast)
## learning: it’s better together.
URL: https://mindiweik.com/blog/learning-its-better-together/
Published: 2024-01-09
## hello & welcome
Thank you for joining me! Together I hope that we will learn something new, add to shared knowledge through conversation, and dive deep into interesting technical topics.
I am a Software Engineer working remotely in the United States. My background is varied (odd jobs through my teens, non-profits, real estate, SaaS tech starting in Customer Success/Support). I altered my career trajectory when I realized just how much I love solving highly technical problems and enjoy consistent learning.
My only regret? I wish I discovered it sooner!
This content will remain free for the foreseeable future! My aim is to learn together.
You can expect varied content about software development, personal development, and various adjacent tech topics from me. Mostly, I will share what I’m learning or thinking about in my tech career.
## what this space is not intended to be:
- for rude comments or negative interactions
- solely for active/current engineers. I read a lot of blogs/articles prior to my own career shift
- for exclusion of anyone trying to learn & grow
## what this space is intended to be:
- to share knowledge and ask curious questions
- to practice constructive criticism
- to engage with the greater technical community
- for anyone interested in the technical topic, regardless of current working status
- for inclusivity - anyone with a desire to learn is welcome!
## keep an eye out!
I look forward to sharing with you all and I hope you’ll engage along this journey.