<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://www.germanvelasco.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.germanvelasco.com/" rel="alternate" type="text/html" /><updated>2026-06-02T10:39:24+00:00</updated><id>https://www.germanvelasco.com/feed.xml</id><title type="html">German Velasco</title><subtitle>Software developer and consultant. Let&apos;s build great software together.</subtitle><author><name>German Velasco</name></author><entry><title type="html">Daily writing retrospective</title><link href="https://www.germanvelasco.com/blog/daily-writing-retrospective" rel="alternate" type="text/html" title="Daily writing retrospective" /><published>2026-06-02T00:00:00+00:00</published><updated>2026-06-02T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/daily-writing-retrospective</id><content type="html" xml:base="https://www.germanvelasco.com/blog/daily-writing-retrospective">&lt;p&gt;Back in April, I decided to write daily for my newsletter. Up to that point, my
newsletter was mostly a delivery mechanism for my blog. But I wanted to change
that. I wanted to write more.&lt;/p&gt;

&lt;p&gt;This is what I said in my first post:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It’s been a while since I’ve written in my newsletter (hope you’re still here!)&lt;/p&gt;

  &lt;p&gt;In this age of AI, I find that I want to write more (not less) in my &lt;em&gt;own&lt;/em&gt;
voice. To me, writing is thinking, and I have a lot of thoughts rolling in my
brain that I want to clarify and solidify – so what better way to do that
than to write?&lt;/p&gt;

  &lt;p&gt;But I have to dust some cobwebs.&lt;/p&gt;

  &lt;p&gt;Well, I love Martin Fowler’s &lt;a href=&quot;https://martinfowler.com/bliki/FrequencyReducesDifficulty.html&quot;&gt;“if it hurts, do it more often”&lt;/a&gt;
aphorism. I frequently say it in programming or business circles. Why shouldn’t
it apply to writing as well?&lt;/p&gt;

  &lt;p&gt;So, I’m going to try to write daily (for 30 days) and see if it sticks. At
least for week days (weekends might be tough). If that’s not interesting to
you, you can always unsubscribe. But before you do so, give me a chance!&lt;/p&gt;

  &lt;p&gt;I hope that what I write is useful to you.&lt;/p&gt;

  &lt;p&gt;More than anything, I’d love to also hear from you.&lt;/p&gt;

  &lt;p&gt;If I write something that interests you or sparks something in your mind, I’d
love to hear it. Maybe this can become more of a conversation than me screaming
into the void.&lt;/p&gt;

  &lt;p&gt;Well… that’s it. Hope you enjoy the ride with me.&lt;/p&gt;

  &lt;p&gt;Best,&lt;/p&gt;

  &lt;p&gt;German&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my typical retrospective format, I’ll ask:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What went well?&lt;/li&gt;
  &lt;li&gt;What went wrong?&lt;/li&gt;
  &lt;li&gt;What could we improve?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-went-well&quot;&gt;What went well?&lt;/h2&gt;

&lt;p&gt;First, the wins.&lt;/p&gt;

&lt;h3 id=&quot;i-wrote-a-lot-more&quot;&gt;I wrote a lot more!&lt;/h3&gt;

&lt;p&gt;I wrote 29 posts in a span of ~40 days. I missed some days. But for the most
part, writing daily – and publishing daily – was a tremendous forcing
function.&lt;/p&gt;

&lt;p&gt;Nothing like a good deadline to force yourself to &lt;em&gt;just ship&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It wasn’t easy. I had to make time for writing (typically at 5am), since I
didn’t have time otherwise. And it took about an hour to write each short post.
But I loved being able to ship daily, even when I wasn’t fully satisfied with
what I wrote. (Don’t let perfect be the enemy of good, right?)&lt;/p&gt;

&lt;h3 id=&quot;i-published-blog-posts&quot;&gt;I published blog posts&lt;/h3&gt;

&lt;p&gt;Out of that daily writing, I also created three blog posts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.germanvelasco.com/blog/managerial-economics-ai-and-software-development&quot;&gt;What Managerial Economics can tell us about AI and Software Development&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.germanvelasco.com/blog/pair-programming-is-a-cheat-code&quot;&gt;Pair-programming is a cheat code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.germanvelasco.com/blog/thinking-about-the-extremes-to-break-out-of-local-maxima&quot;&gt;Thinking about the extremes to break out of local maxima&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;it-helped-me-think&quot;&gt;It helped me think&lt;/h3&gt;

&lt;p&gt;In my first post I said “to me writing is thinking”. And boy did it get me
thinking.&lt;/p&gt;

&lt;p&gt;Because I had to write daily, I was constantly thinking of what I would write
the next day, jotting down some ideas and notes, and it did help me work through
some of the “thoughts” I had in my head (the ones I mentioned in my first post).&lt;/p&gt;

&lt;h3 id=&quot;it-helped-me-connect-with-readers&quot;&gt;It helped me connect with readers&lt;/h3&gt;

&lt;p&gt;One of the coolest things was when a reader responded to a post. I love
connecting with others over ideas. And a few people responded to some of my
posts excitedly agreeing, asking questions, or offering their perspective.&lt;/p&gt;

&lt;p&gt;It was honestly one of the best surprises about writing daily. Before I started,
I didn’t know what to expect: either utter silence or perhaps outrage (those are
the fears in our heads). Instead, people were interested and excited.&lt;/p&gt;

&lt;p&gt;To those of you who wrote back, thank you!&lt;/p&gt;

&lt;h3 id=&quot;my-newsletter-doesnt-just-have-to-be-a-delivery-mechanism&quot;&gt;My newsletter doesn’t just have to be a delivery mechanism&lt;/h3&gt;

&lt;p&gt;Because I connected with readers, and because I have now made my newsletter into
a vehicle that is more than just delivering blog posts, I feel comfortable
writing there.&lt;/p&gt;

&lt;p&gt;That may not seem big, but it’s a huge win for me.&lt;/p&gt;

&lt;p&gt;Before this exercise, I felt like my newsletter had an implicit agreement that
it was purely a delivery mechanism for blog posts. After 29 posts, I think the
people who only wanted it for that reason have unsubscribed. :)&lt;/p&gt;

&lt;p&gt;So, overall, I’m very glad I did it. But I want to make some changes. So, on to
the what went wrong section.&lt;/p&gt;

&lt;h2 id=&quot;what-went-wrong&quot;&gt;What went wrong?&lt;/h2&gt;

&lt;h3 id=&quot;its-costly&quot;&gt;It’s costly&lt;/h3&gt;

&lt;p&gt;I &lt;em&gt;loved&lt;/em&gt; the forcing function of writing daily. But the reality is that it is
hard to always wake up at 5am and write.&lt;/p&gt;

&lt;p&gt;Sleep is very important to me. Due to previous health issues, I try to
prioritize sleep as much as possible, and my kids are also grateful when I’m not
grumpy and tired. And though I wish I didn’t need as much sleep as I do, I think
making sure I sleep well is the right trade-off.&lt;/p&gt;

&lt;p&gt;I also have a bad habit of wanting to do too many things. So, as I was writing
daily, I was also exploring new AI workflows, recording a couple of remote
pairing sessions (which I’m still editing), and trying to get more information
on how other teams are using AI. Oh, and I also had to do my regular consulting
job. That’s a lot to fit into a single week.&lt;/p&gt;

&lt;p&gt;So, even though I love writing daily, there are days when it is more important
that I get an extra hour of sleep or that I edit a video.&lt;/p&gt;

&lt;h3 id=&quot;i-forgot-to-decouple-deployments-from-releasing&quot;&gt;I forgot to decouple deployments from releasing&lt;/h3&gt;

&lt;p&gt;Another huge problem was that by writing daily, I meant &lt;em&gt;publishing&lt;/em&gt; daily.&lt;/p&gt;

&lt;p&gt;In software, I always encourage teams to decouple deployments from releasing
software to customers. We tend to use feature flags for that. That way, you can
deploy whenever you want – ideally continuously. But you only release a feature
when it’s ready.&lt;/p&gt;

&lt;p&gt;Well, if I’d done that with writing, I could have written daily but only
published when I felt ready. In that case, I might have felt differently about
some of my posts.&lt;/p&gt;

&lt;p&gt;Which brings me to the next point.&lt;/p&gt;

&lt;h3 id=&quot;some-ideas-need-more-space-and-time&quot;&gt;Some ideas need more space and time&lt;/h3&gt;

&lt;p&gt;Publishing daily meant I couldn’t fully work out some of the thornier ideas in
my head.&lt;/p&gt;

&lt;p&gt;Some writing just doesn’t fit into a single day – or into a single email, for
that matter. Many thoughts are just complicated, and I feel like I have to
untangle them before I can write about them.&lt;/p&gt;

&lt;p&gt;I mean, that’s part of the beauty of writing: it forces you to work out those
complicated topics until you can explain them simply. But because I had to
publish something &lt;em&gt;every&lt;/em&gt; day, I just couldn’t spend the time I needed on the
thornier ideas.&lt;/p&gt;

&lt;p&gt;So, publishing daily sometimes forced me to write about a simpler idea at the
cost of refining a thornier one.&lt;/p&gt;

&lt;h3 id=&quot;posts-are-siloed&quot;&gt;Posts are siloed&lt;/h3&gt;

&lt;p&gt;This is a good and a bad thing.&lt;/p&gt;

&lt;p&gt;Because posts are not available in the general internet (unlike a blog post is),
I felt more okay with publishing things that I wouldn’t have otherwise. I don’t
mean that the posts were bad, but there’s an internal pressure when I publish a
blog post that I have to make sure it’s &lt;em&gt;really&lt;/em&gt; good because some unknown
masses will read it.&lt;/p&gt;

&lt;p&gt;In reality, for all I know, more people actually read the newsletter posts than
my blog posts. But because the blog posts are “out there”, I feel that pressure.
And that pressure can lead to &lt;em&gt;not&lt;/em&gt; writing. So, the siloing can be a good
thing.&lt;/p&gt;

&lt;p&gt;But it’s definitely &lt;em&gt;sad&lt;/em&gt; that they’re siloed.&lt;/p&gt;

&lt;p&gt;I want to write to communicate with others. And I &lt;em&gt;love&lt;/em&gt; blogs because to me
they are part of the beautiful fabric of the internet. People sharing learnings,
lessons, and ideas without gatekeeping. So, I’m a little sad that I have things
written that live in emails and cannot be easily shared.&lt;/p&gt;

&lt;h2 id=&quot;what-could-we-improve&quot;&gt;What could we improve?&lt;/h2&gt;

&lt;p&gt;Having said all that, I hope to make some tweaks.&lt;/p&gt;

&lt;p&gt;I won’t continue to put the must-publish-daily pressure on myself. I wish I
didn’t have to remove that (I like the forcing function), but I have to
prioritize other things right now.&lt;/p&gt;

&lt;p&gt;But that doesn’t mean I’ll stop writing!&lt;/p&gt;

&lt;p&gt;On the contrary, I hope to continue to write more. I’ll still make 5am my
writing time. It works most days. I just might not be able to hit it daily.&lt;/p&gt;

&lt;p&gt;And I will continue writing in my newsletter. But I hope to publish a lot more
to my blog as well. In a way, I would love it if I could write daily on my blog.
That’s a bit wild, but I think it’s the ideal.&lt;/p&gt;

&lt;p&gt;And I also hope to make my newsletter more of a 2-way channel.&lt;/p&gt;

&lt;p&gt;I want to continue connecting directly with readers of the newsletter. That was
one of my favorite parts of writing daily, so I’m really hoping I continue doing
that.&lt;/p&gt;

&lt;h2 id=&quot;write-to-me-or-send-me-your-blog&quot;&gt;Write to me or send me your blog!&lt;/h2&gt;

&lt;p&gt;Finally, some parting thoughts.&lt;/p&gt;

&lt;p&gt;First, if you’re reading this, thanks for reading! Thank you for giving me some
of your precious time.&lt;/p&gt;

&lt;p&gt;Second, I encourage you to write more! (Make sure &lt;em&gt;you&lt;/em&gt; write, not the robots!)&lt;/p&gt;

&lt;p&gt;If you’d like a reader, send me an email and send me your blog. I love
discovering new blogs to read.&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="software-development" /><category term="writing" /><summary type="html">Back in April, I decided to write daily for my newsletter. Up to that point, my newsletter was mostly a delivery mechanism for my blog. But I wanted to change that. I wanted to write more.</summary></entry><entry><title type="html">Thinking about the extremes to break out of local maxima</title><link href="https://www.germanvelasco.com/blog/thinking-about-the-extremes-to-break-out-of-local-maxima" rel="alternate" type="text/html" title="Thinking about the extremes to break out of local maxima" /><published>2026-05-22T00:00:00+00:00</published><updated>2026-05-22T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/thinking-about-the-extremes-to-break-out-of-local-maxima</id><content type="html" xml:base="https://www.germanvelasco.com/blog/thinking-about-the-extremes-to-break-out-of-local-maxima">&lt;p&gt;Thinking about the extremes is a powerful tool to help refine our intuition,
unearth second-order effects, discover novel ways of doing things, and generally
find alternate solutions to problems when we’re stuck.&lt;/p&gt;

&lt;p&gt;Think of it like this: you’re a person trying to climb a hill.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/local-maxima/marginal-step-takes-you-up.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One way to do that is to take a step in any direction. If you discover that the
next step is higher than your previous one, great! You’re making progress.&lt;/p&gt;

&lt;p&gt;You repeat that process over and over again, taking one step at a time. Each
time, you check if you’re higher than before. If you are, you’re going uphill!&lt;/p&gt;

&lt;p&gt;That’s marginal improvement, and it’s typically good. After only a finite number
of steps, you’ll reach the top of the hill.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/local-maxima/top-of-the-hill.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But once you’re at the top, any step you take will be a step downhill. You’ve
reached &lt;em&gt;a&lt;/em&gt; maximum.&lt;/p&gt;

&lt;p&gt;How can you reach higher places? You can’t use the same “one small step”
mentality. You actually need to change your approach.&lt;/p&gt;

&lt;p&gt;And that’s where thinking about extremes can be helpful.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/local-maxima/global-maxima.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s only by thinking, “how can I climb Mount Everest?” that you realize you
might need to get on a plane and fly to Nepal.&lt;/p&gt;

&lt;p&gt;I think the same is true in business and software development. You can often get
very far by making the next marginal improvement. One step at a time is good!
You might even reach some heights. But at some point, you’ll likely get stuck in
a local maximum. And it’s at that point that thinking about the extremes can
help.&lt;/p&gt;

&lt;p&gt;Consider this example. If you’re only trying to grow your revenue by 3% this
year, you’ll probably focus on marginal improvements. But if you start thinking
about how you can double or triple your revenue this year, you’ll come up with
some wild, new ideas because you &lt;em&gt;have&lt;/em&gt; to. There’s no other way to reach that
goal.&lt;/p&gt;

&lt;p&gt;Or think about query optimization in software development. You might try to
optimize the query as much as possible. But at some point, you can’t make it any
faster (you’ve hit a local minimum). If you think about the extreme you might
ask, what if we could avoid making the database call altogether? You might
discover a way to support the feature without that query or a different way to
get the same information.&lt;/p&gt;

&lt;p&gt;Steady progress is good and important. But every once in a while, you get stuck.
That’s a good time to start thinking about the extremes.&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="mental-models" /><category term="business" /><category term="software-development" /><summary type="html">Thinking about the extremes is a powerful tool to help refine our intuition, unearth second-order effects, discover novel ways of doing things, and generally find alternate solutions to problems when we&apos;re stuck.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.germanvelasco.com/images/local-maxima/top-of-the-hill.png" /><media:content medium="image" url="https://www.germanvelasco.com/images/local-maxima/top-of-the-hill.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Pair-programming is a cheat code</title><link href="https://www.germanvelasco.com/blog/pair-programming-is-a-cheat-code" rel="alternate" type="text/html" title="Pair-programming is a cheat code" /><published>2026-05-01T00:00:00+00:00</published><updated>2026-05-01T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/pair-programming-is-a-cheat-code</id><content type="html" xml:base="https://www.germanvelasco.com/blog/pair-programming-is-a-cheat-code">&lt;p&gt;&lt;img src=&quot;/images/pair-programming-is-a-cheat-code.png&quot; alt=&quot;pair-programming is a cheat code written on a whiteboard&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I had that written on my office whiteboard for a while last year.&lt;/p&gt;

&lt;p&gt;I had just finished working with a client for over a year where the whole team
pair-programmed full-time (we also did a lot of mobbing).&lt;/p&gt;

&lt;p&gt;I was then starting work with a new client, and I wanted to remind myself to
push for pair-programming as much as possible because it is a cheat code for
developers &lt;em&gt;AND&lt;/em&gt; for businesses.&lt;/p&gt;

&lt;p&gt;Here are some of “cheats” pair-programming gives you.&lt;/p&gt;

&lt;h2 id=&quot;pair-programming-increases-decision-speed&quot;&gt;Pair-programming increases decision speed&lt;/h2&gt;

&lt;p&gt;I think this is one of the most underrated benefits of pair-programming.&lt;/p&gt;

&lt;p&gt;When we’re building software, there are countless decisions we have to make.
Those decisions take time. And frequently, they take coordination across teams.
That’s really costly.&lt;/p&gt;

&lt;p&gt;And those decisions tend to have ramifications that compound.&lt;/p&gt;

&lt;p&gt;It’s typically cheaper for a business to change course early in the
feature-development lifecycle. So, you want to arrive at the right decision as
early as possible. With pair-programming, you have two people with full context
able to bounce ideas off each other, challenge assumptions, and refine solutions
quickly.&lt;/p&gt;

&lt;p&gt;Compare that to finishing your feature, opening a pull-request, and waiting a
few hours (or days) for someone to &lt;em&gt;then&lt;/em&gt; question a piece of work. You go back
and forth on alternatives (what you would’ve done with a pair at the moment you
were coding that thing), and finally, you have to rewrite large pieces of the
code because the decision made early informed the design of the whole feature.&lt;/p&gt;

&lt;h2 id=&quot;code-review-is-done-in-real-time-with-full-context&quot;&gt;Code-review is done in real-time with full context&lt;/h2&gt;

&lt;p&gt;It’s sad that most companies have a culture of doing code-review through
pull-requests. GitHub has been a huge influence in that. And it’s a great model
for low-trust environments (where PR author and reviewer don’t know each other).
There’s high friction, and that’s a good thing.&lt;/p&gt;

&lt;p&gt;But your company should be a high-trust environment. You know your team. You
sync with them daily! So, to do code-review in the same slow, async way we do
with open-source code seems suboptimal.&lt;/p&gt;

&lt;p&gt;And when you’re reviewing a pull-request, you’re often missing context for the
work that’s being done, decisions made along the way, and sometimes why the team
is even building a given feature. So, as the reviewer, you have to either gain a
lot of context to review the code well, or your review is worse than it would be
if you had all that context.&lt;/p&gt;

&lt;p&gt;Well, do you know who has all that context? The pair. And the pair can review
the code &lt;em&gt;as they write it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That doesn’t mean others can’t look at the code. If you’re working on something
gnarly that you want more eyes on, you can always tap another pair and ask them
to do a review on a call. All of a sudden you have 4 people (2 pairs) reviewing
the code while it’s being written. Very effective and efficient.&lt;/p&gt;

&lt;h2 id=&quot;knowledge-transfer-happens-naturally&quot;&gt;Knowledge transfer happens naturally&lt;/h2&gt;

&lt;p&gt;In programming, we’re always talking about the &lt;a href=&quot;https://en.wikipedia.org/wiki/Bus_factor&quot;&gt;bus factor&lt;/a&gt; or domain knowledge
silos. We don’t want a single developer to “own” a piece of code, and if they
leave the company, get sick, or get hit by a bus, nobody else knows how to touch
it.&lt;/p&gt;

&lt;p&gt;(Also, note that they’re likely the &lt;em&gt;only&lt;/em&gt; person who can review PRs related to
that area of the codebase. Makes that PR problem even worse!)&lt;/p&gt;

&lt;p&gt;So, teams tend to need to put a lot of effort into artifacts (documentation,
videos, etc.) that can share that knowledge with other developers in case that
bus really comes.&lt;/p&gt;

&lt;p&gt;But documentation is costly and at best a proxy. Usually, it’s out of date and
incomplete.&lt;/p&gt;

&lt;p&gt;The code is the source of truth and the full specification of what our
application currently does. So, it would be nice if we could rely on that. Of
course, it takes time for people to gain knowledge of a particular domain in the
codebase.&lt;/p&gt;

&lt;p&gt;That’s where switching pairs frequently helps tremendously.&lt;/p&gt;

&lt;p&gt;If you frequently switch who you pair with, that siloed context is immediately
spread out across the team. The more you switch pairs, the more the context
spreads. You don’t have to do anything special (other than rotate pairs).&lt;/p&gt;

&lt;h2 id=&quot;pair-programming-is-the-best-way-to-teach-junior-developers&quot;&gt;Pair-programming is the best way to teach junior developers&lt;/h2&gt;

&lt;p&gt;And since sharing context is so easy with pair-programming, it’s also one of the
best ways to teach junior developers. I’ve seen junior developers get good at an
alarmingly fast pace. And they’ll ask the best questions, questions that frankly
make you (the senior developer) better at your craft.&lt;/p&gt;

&lt;h2 id=&quot;pair-programming-helps-developers-learn-better-ways-to-work-and-solve-problems&quot;&gt;Pair-programming helps developers learn better ways to work and solve problems&lt;/h2&gt;

&lt;p&gt;The previous point leads to this one.&lt;/p&gt;

&lt;p&gt;When you’re pair-programming, people have insight into &lt;em&gt;how&lt;/em&gt; you work, not just
the final code in a pull-request. That is phenomenal for two reasons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;a) Others learn from things you do. Things they didn’t even know were
possible! It opens minds to whole new categories.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;b) You learn from questions your pair asks! It’s amazing how a simple question
can lead you to realize you haven’t re-evaluated the best way to solve a problem
in many years.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(And note that we’re all switching between (a) and (b) all the time. I learn
from you and you learn from me. And we both ask questions of each other.)&lt;/p&gt;

&lt;h2 id=&quot;is-it-still-useful-in-an-ai-world&quot;&gt;Is it still useful in an AI world?&lt;/h2&gt;

&lt;p&gt;Yes, yes it is!&lt;/p&gt;

&lt;p&gt;Programming with AI is changing rapidly. How can your team learn to use these
new tools as fast as possible?&lt;/p&gt;

&lt;p&gt;I think there’s no better way to do that (and to develop best practices) than to
pair a lot and rotate pairs frequently.&lt;/p&gt;

&lt;p&gt;Think about it the other way. The more you silo information by &lt;em&gt;not&lt;/em&gt; pairing,
the slower the spread of knowledge and good practices. So, pair-programming is
even &lt;em&gt;more&lt;/em&gt; (not less) useful in this world full of LLMs.&lt;/p&gt;

&lt;h2 id=&quot;give-it-a-try&quot;&gt;Give it a try&lt;/h2&gt;

&lt;p&gt;There are more reasons why pair-programming is a cheat code, but hopefully those
are good enough to give you a taste of the value it can bring to your team.&lt;/p&gt;

&lt;p&gt;If your team doesn’t do pair-programming, I encourage you to give it a &lt;em&gt;real&lt;/em&gt;
try. Read a little about how to do it well (and there are ways to do it well and
ways to do it poorly). And stick with it. It’s a skill that needs to be learned,
so don’t expect to be immediately proficient at it.&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="pair-programming" /><summary type="html">I had that written on my office whiteboard for a while last year. I had just finished working with a client for over a year where the whole team pair-programmed full-time (we also did a lot of mobbing). I was then starting work with a new client, and I wanted to remind myself to push for pair-programming as much as possible because it is a cheat code for developers AND for businesses. Here are some of &quot;cheats&quot; pair-programming gives you.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.germanvelasco.com/images/pair-programming-is-a-cheat-code.png" /><media:content medium="image" url="https://www.germanvelasco.com/images/pair-programming-is-a-cheat-code.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What Managerial Economics can tell us about AI and Software Development</title><link href="https://www.germanvelasco.com/blog/managerial-economics-ai-and-software-development" rel="alternate" type="text/html" title="What Managerial Economics can tell us about AI and Software Development" /><published>2026-04-23T00:00:00+00:00</published><updated>2026-04-23T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/managerial-economics-ai-and-software-development</id><content type="html" xml:base="https://www.germanvelasco.com/blog/managerial-economics-ai-and-software-development">&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Charlie_Munger&quot;&gt;Charlie Munger&lt;/a&gt; used to analyze investments by having a latticework of
interdisciplinary mental models of how the world worked.&lt;/p&gt;

&lt;p&gt;And I love the idea.&lt;/p&gt;

&lt;p&gt;I don’t have as many as he did (I believe he had 80-90 mental models), and many
of mine come from my Economics background. So, perhaps they’re not very “well
rounded”. But they sure are useful!&lt;/p&gt;

&lt;p&gt;The mental model I’ve been thinking about lately comes from Managerial
Economics. It’s a framework for analyzing how a firm can trade off capital and
labor to produce goods. And I think it’s helpful for Software Developers in this
new age of AI.&lt;/p&gt;

&lt;p&gt;It can help us think about questions like these:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What happens if the cost of LLMs drops dramatically?&lt;/li&gt;
  &lt;li&gt;What happens if AI is a great replacement for Software Developers?&lt;/li&gt;
  &lt;li&gt;What happens if AI is merely a tool for Software Developers?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, hold on to your hat. To fully understand the framework, we have to have the
basics down. So, we have to take a detour into &lt;a href=&quot;https://en.wikipedia.org/wiki/Isoquant&quot;&gt;isoquant&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Isocost&quot;&gt;isocost&lt;/a&gt; lines. I
promise it won’t be &lt;em&gt;too&lt;/em&gt; painful.&lt;/p&gt;

&lt;p&gt;And in each section, we’ll see how we can apply it to our analysis of Software
Developers and AI:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#isoquant-curves-and-isocost-lines&quot;&gt;Isoquant curves and Isocost Lines&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#perfect-substitutes&quot;&gt;Perfect substitutes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#perfect-complements&quot;&gt;Perfect complements&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#are-ai-and-software-developers-substitutes-or-complements&quot;&gt;Are AI and Software Developers substitutes or complements?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;isoquant-curves-and-isocost-lines&quot;&gt;Isoquant curves and Isocost Lines&lt;/h2&gt;

&lt;h3 id=&quot;isoquant-curves&quot;&gt;Isoquant curves&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Isoquant&quot;&gt;Isoquants&lt;/a&gt; are the lines along which one input can be substituted for another
&lt;em&gt;while maintaining the same level of output&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And they’re typically drawn something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/three-isoquant-curves.svg&quot; alt=&quot;isoquant curve&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That graph has two inputs (X and Y) and it shows 3 isoquants (Q1, Q2, Q3). The
further up and to the right the isoquant, the higher the output (so, Q3 &amp;gt; Q2 &amp;gt;
Q1). And the &lt;a href=&quot;https://en.wikipedia.org/wiki/Marginal_rate_of_technical_substitution&quot;&gt;Marginal Rate of Technical Substitution (MRTS)&lt;/a&gt; is the slope
at a given point on one of those curves.&lt;/p&gt;

&lt;p&gt;At its core, MRTS compares 2 inputs to each other, and it tells you how much of
one input you need to reduce to get one more unit of the other &lt;em&gt;while keeping
output constant&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That “&lt;em&gt;while keeping output constant&lt;/em&gt;” is important.&lt;/p&gt;

&lt;p&gt;In our analysis, we’ll consider the two inputs to be capital and labor – those
are both &lt;em&gt;very&lt;/em&gt; generic, but they also map somewhat neatly to LLMs/AI and
Software Developers. And you can consider output to be anything the business
owner is interested in – number of features, a stable, bug-free product, number
of stories done, etc.&lt;/p&gt;

&lt;p&gt;The isoquant curves tend to have a curvature like that (convex to the origin)
because along the curve you have diminishing MRTS. That is, typically, you
cannot simply go all capital and no labor (or vice versa) and still have the
same output. You need both.&lt;/p&gt;

&lt;p&gt;It’s helpful to think about it in the extremes to see how it works: if you have
zero capital and all labor, adding one unit of capital might be &lt;em&gt;very&lt;/em&gt;
beneficial. And it would allow the business to reduce labor by a lot (while
keeping output constant).&lt;/p&gt;

&lt;p&gt;For just a second, let’s forget AIs and Software Devs. Think instead of 10
people digging a hole with spoons, and then you introduce a shovel. You might
only need 2 people with 2 shovels to dig the same number of holes.&lt;/p&gt;

&lt;p&gt;But at some point, the value of adding more capital doesn’t help your output.
A single person with 3 shovels won’t dig any more holes. So the rate at which
you trade-off labor for capital diminishes the further you go to the extremes.&lt;/p&gt;

&lt;p&gt;It’s worth noting, all things being equal, a business owner would typically like
to be in higher isoquant (to make more holes, produce more, and make more
money). But &lt;em&gt;given&lt;/em&gt; a fixed output, they can only move &lt;em&gt;within&lt;/em&gt; a given
isoquant. So, the business owner has to choose how much labor and capital to
have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But how do they choose?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For that, we want to look at cost.&lt;/p&gt;

&lt;h3 id=&quot;isocost-lines&quot;&gt;Isocost Lines&lt;/h3&gt;

&lt;p&gt;We assume the business has a budget constraint. That is, we assume that they
want to make more money, and if they produced more “output” (however we define
that), they would make more of it.&lt;/p&gt;

&lt;p&gt;But they have costs – and in our particular case, they have capital and labor
costs.&lt;/p&gt;

&lt;p&gt;So, we would have something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C = C_labor * Q_labor + C_capital * Q_capital
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or in typical Economics notation:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C = rK + wL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That is cost is equal to the wage &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt;, times the quantity of labor &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L&lt;/code&gt;, plus the
cost of renting capital &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;r&lt;/code&gt; times the quantity of capital &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;K&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An isocost line shows all combinations of capital and labor that cost the same
C.&lt;/p&gt;

&lt;p&gt;We can neatly graph an isocost line (notice the constant negative slope):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/isocost-line.svg&quot; alt=&quot;isocost line&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;how-to-choose-optimum-capital-labor-combination&quot;&gt;How to choose optimum capital-labor combination&lt;/h3&gt;

&lt;p&gt;So, as with much of modern economics, the question becomes one of optimization.&lt;/p&gt;

&lt;p&gt;What’s the optimal use of capital and labor (to minimize cost) for a &lt;em&gt;given&lt;/em&gt;
level of output?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/optimal-point-in-isoquant-and-isocost.svg&quot; alt=&quot;optimal point when isoquant and isocost lines intersect&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The solution to that is the point at which MRTS is equal to the slope of the
isoquant line. In other words, the point at which the isoquant curve touches the
isocost line in the graph above (at that point, the slopes are the same).&lt;/p&gt;

&lt;p&gt;At that point, the business owner is keeping their cost C, and they have the
best combination of capital and labor to get that level of output.&lt;/p&gt;

&lt;h3 id=&quot;capital-ai-and-labor-software-developers&quot;&gt;Capital (AI) and Labor (Software Developers)&lt;/h3&gt;

&lt;p&gt;So, how do we apply this to AI and Software Devs?&lt;/p&gt;

&lt;p&gt;Well, remember this is just a model. So we’re making many simplifications and
assumptions.&lt;/p&gt;

&lt;p&gt;If we treat Software Developers in the pre LLM world as labor, and AI as a new
capital (think of a new shovel), we have to realize that businesses are going to
start making a trade-off between the amount of capital and labor they use.&lt;/p&gt;

&lt;p&gt;In particular, they’ll likely grow the amount they spend on AI while decreasing
the amount they spend on Software Developers (all other things being equal).&lt;/p&gt;

&lt;p&gt;That doesn’t mean that software developers will get paid less (it might, but
that’s not the implication here). It means that for a given software product
(keeping output constant), a business owner will likely spend more of their
“software” budget on AI and less on Software Developers than they were doing
previously. And they will do so until we reach a new equilibrium point where the
MRTS between labor and capital is equal to the slope of the isocost line.&lt;/p&gt;

&lt;p&gt;I’ve actually seen that already happening in companies. Their budgets planned
for 2026 include fewer developers than originally anticipated and larger AI
budgets. So, they’re actively moving money previously allocated for new hires
into paying for LLMs.&lt;/p&gt;

&lt;p&gt;Now, there are more implications and further analysis we can do here. For
example, we can use the model to understand what happens if the cost of LLMs
drops radically.&lt;/p&gt;

&lt;p&gt;But first, I think it’s helpful to think of the extremes in order to develop
more intuition around the framework.&lt;/p&gt;

&lt;p&gt;To that end, we’ll look at two scenarios:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What happens if LLMs and Software Developers are perfect substitutes (the dreaded dark factory)&lt;/li&gt;
  &lt;li&gt;What happens if they are perfect complements (they just make us devs more productive).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;perfect-substitutes&quot;&gt;Perfect substitutes&lt;/h2&gt;

&lt;p&gt;Let’s first see a scenario where AI is a perfect substitute for Software
Developers.&lt;/p&gt;

&lt;p&gt;That’s the dreaded &lt;a href=&quot;https://en.wikipedia.org/wiki/Lights_out_(manufacturing)&quot;&gt;dark factory&lt;/a&gt; where developers are completely replaced by
AI. You can imagine several versions of this, but one would be a completely
automated system where a PM just writes stories and AI pulls them, implements
them, reviews them, and ships them.&lt;/p&gt;

&lt;p&gt;When that’s the case, an isoquant looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/perfect-substitutes-isoquant.svg&quot; alt=&quot;perfect substitutes isoquant&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That &lt;em&gt;isn’t&lt;/em&gt; an isocost line that we just drew. It is an isoquant “curve” (and
recall they used to be curved).&lt;/p&gt;

&lt;p&gt;That isoquant has a perfect 1-1 relationship between the two inputs (or the MRTS
is constant). So, you can replace one developer with a fixed unit of AI “stuff”
and you’ll still get the same output. (what exactly is a unit of AI, I don’t
know.)&lt;/p&gt;

&lt;p&gt;So, if that is an isoquant, where does it intersect the isocost line?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/perfect-substitutes-isoquant-and-isocost.svg&quot; alt=&quot;perfect substitutes isoquant intersects isocost at all K&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the isocost line intersects the isoquant at all capital (K), no
labor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is that?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Remember that a business will always like to be on the highest isoquant
(furthest up and to the right) possible (assuming making more stuff makes more
money). So, they minimize cost subject to being on the highest isoquant
possible.&lt;/p&gt;

&lt;p&gt;We can see how much &lt;em&gt;more&lt;/em&gt; they would have to spend to reach the same level of
output if they spent it all on labor.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/perfect-substitutes-isoquant-and-isocost-all-labor.svg&quot; alt=&quot;how much higher isocost line has to be to intersect isoquant at all L&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the company would have to spend far more money to keep the same
level of output if they wanted to spend it all on labor.&lt;/p&gt;

&lt;p&gt;So, yes, in the perfect substitutes case, it’s a sad day for Software Developers.
The businesses need to adapt to the changing cost landscape (they’re competing
with other businesses doing the same), so they would ultimately fully replace
labor.&lt;/p&gt;

&lt;p&gt;Now, is that what’s happening with AI? I don’t know.&lt;/p&gt;

&lt;p&gt;But let’s take a look at what happens if AI and Software Developers are perfect
complements instead. It paints a very different picture.&lt;/p&gt;

&lt;h2 id=&quot;perfect-complements&quot;&gt;Perfect complements&lt;/h2&gt;

&lt;p&gt;Let’s go back to isoquants. The shape of isoquants for perfect complements looks
like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/perfect-complements.svg&quot; alt=&quot;L-shaped isoquant shows perfect complements&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That is, the perfect complements isoquant curve has an L shape. That’s because a
business always needs both labor and capital in the same proportions.&lt;/p&gt;

&lt;p&gt;When we combine that with an isocost line (assume the business has fixed
budget), we get the following:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/perfect-complements-with-isocost.svg&quot; alt=&quot;perfect complements isoquant intersects isocost at corner&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In other words, given a fixed cost, the highest isoquant (output) a business can
reach is at the bottom-left corner of the isoquant L-curve.&lt;/p&gt;

&lt;p&gt;Or put the other way around, given a fixed level of output, the lowest isocost
line (i.e. minimizing cost) that reaches such output is that corner.&lt;/p&gt;

&lt;p&gt;So, in a world of perfect complements, the prices of labor and capital are
somewhat irrelevant when allocating resources. The business is simply
constrained by their budget and the need to have capital and labor in an exact
ratio. They cannot trade-off more capital for less labor (or vice versa) because
both are needed in exact proportions.&lt;/p&gt;

&lt;p&gt;Maybe one more graph will make it super clear. Let’s say the business wants to
produce more output. It cannot simply add more capital &lt;em&gt;or&lt;/em&gt; more labor. It needs
to add both! And since they’re perfect complements, they have to be added in the
same proportions.&lt;/p&gt;

&lt;p&gt;That looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/perfect-complements-higher-isoquant.svg&quot; alt=&quot;perfect complements can only reach higher isoquant with higher isocost&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, in order to reach a higher level of output (isoquant 2), the
business &lt;em&gt;has&lt;/em&gt; to spend more money (isocost 2). And it does so in the same ratio
of capital to labor.&lt;/p&gt;

&lt;h3 id=&quot;ai-and-software-developers&quot;&gt;AI and Software Developers&lt;/h3&gt;

&lt;p&gt;Let’s now jump back to AI and Software Developers.&lt;/p&gt;

&lt;p&gt;If AI and Software Developers are perfect complements, AI is a tool (+ machines,
compute, etc.) for Software Developers.&lt;/p&gt;

&lt;p&gt;But you cannot simply “deploy” AI &lt;em&gt;without&lt;/em&gt; Software Developers. So, companies
that want to produce more (e.g. ship more software) will need to increase their
budgets to hire &lt;em&gt;more&lt;/em&gt; developers &lt;em&gt;and&lt;/em&gt; pay for &lt;em&gt;more&lt;/em&gt; AI for those developers.&lt;/p&gt;

&lt;p&gt;They can’t wholesale replace one with the other.&lt;/p&gt;

&lt;p&gt;A decent example of complements are Software Developers and laptops. When you
hire a new developer, you probably need to buy them a new laptop. 10 developers
with 2 laptops doesn’t help you a lot. And 2 developers with 10 laptops seems
like a waste of money. You want roughly one laptop per developer.&lt;/p&gt;

&lt;h2 id=&quot;are-ai-and-software-developers-substitutes-or-complements&quot;&gt;Are AI and Software Developers substitutes or complements?&lt;/h2&gt;

&lt;p&gt;Now that we’ve seen how &lt;a href=&quot;https://en.wikipedia.org/wiki/Isoquant&quot;&gt;isoquant&lt;/a&gt; curves and &lt;a href=&quot;https://en.wikipedia.org/wiki/Isocost&quot;&gt;isocost&lt;/a&gt; lines behave at the
extremes (when capital and labor are perfect substitutes and perfect
complements), we can ask the question: are AI and Software Developers closer to
substitutes or complements?&lt;/p&gt;

&lt;p&gt;My guess (but it’s just a guess) is that it’ll be somewhere in between the two.
At first, they’ll be more like substitutes. But in the long-run, they’ll be more
like complements.&lt;/p&gt;

&lt;p&gt;I have other reasons for that belief (this mental model doesn’t tell us about
that). But that’s still my guess.&lt;/p&gt;

&lt;p&gt;So, I would probably model it like a “normal” isoquant curve (convex to the
origin):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/isoquant-convex-to-origin.svg&quot; alt=&quot;Normal isoquant. Convex to origin&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In that case, what is happening is that LLMs are a new type of technology that
will shift the relative balance between capital and labor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But that doesn’t mean that &lt;em&gt;less&lt;/em&gt; developers will be needed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the short-run, that might be the case. Like I mentioned, I’ve seen some
companies reallocate some of their hiring budget towards buying tokens for their
existing team.&lt;/p&gt;

&lt;p&gt;But it’s typical for people to think of the world as a fixed “pie”. So, when new
technologies are introduced, they’re worried their share of the pie will be
taken.&lt;/p&gt;

&lt;p&gt;When, in fact, technologies tend to increase the pie. So, it’s possible &lt;em&gt;more&lt;/em&gt;
(not less) developers will be needed (and that can be a bit surprising). Perhaps
the job will change (it is seemingly already changing), but developers would
still be needed, and in higher quantities.&lt;/p&gt;

&lt;p&gt;We can actually see that with isoquant curves and isocost lines by considering
what happens as LLMs get cheaper (assuming they get cheaper).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ai-and-software-dev-from-econ/lower-K-leading-to-higher-Kq-and-Lq.svg&quot; alt=&quot;K gets cheaper. Isocost changes. Gets higher isoquant curve. Leading to higher Kq and Lq&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see in that graph, LLMs getting cheaper means that the isocost line
changes. That allows the business to reach a higher isoquant but only by &lt;em&gt;also&lt;/em&gt;
increasing the quantity of labor.&lt;/p&gt;

&lt;p&gt;You see that the lower cost allows the business to move from Kq1 to Kq2 and Lq1
to Lq2. In other words, the business will employ more developers and use more AI
&lt;em&gt;with the same cost&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Of course, by now you might have noticed that how &lt;em&gt;much&lt;/em&gt; those quantities change
(i.e. the relative values of Kq2-Kq1 and Lq2-Lq1) depends on the shape of the
isoquant curve. Based on how I drew the isoquant curves in the graph, the
increase in the quantity of AI used (change in Kq) is greater than the increase
in the quantity of developers (change in Lq). Both increase, but capital
increases more (which is intuitive since that’s what got cheaper).&lt;/p&gt;

&lt;p&gt;But that’s where the model stops and real world begins.&lt;/p&gt;

&lt;p&gt;Those isoquant curves are nice and smooth and convex to the origin. Real-world
companies don’t have a perfect mathematical equation that they minimize to
figure out where MRTS is equal to the slope of the isoquant curve.&lt;/p&gt;

&lt;p&gt;Instead, businesses try different combinations, producing different levels of
output, and adjusting as revenues and costs change. But, as a rule, businesses
&lt;em&gt;will&lt;/em&gt; adjust because they have incentives to do so. They want to stay around
and (hopefully) be profitable. So, I think changes are inevitable.&lt;/p&gt;

&lt;p&gt;I think there’s more analysis that we could do. But I imagine you’ve had enough
of isoquant curves, isocost lines, and MRTS for now. Still, I hope it’s a
helpful mental model you can use now and in the future.&lt;/p&gt;

&lt;p&gt;I’d love to hear what you think about the current state of AI and Software
Developers. What’s the reality you are seeing now and the future you think is
coming? &lt;a href=&quot;mailto:german@germanvelasco.com&quot;&gt;Shoot me an email&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And thanks for sticking around!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;#&quot;&gt;Back to the top 👆&lt;/a&gt;&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="economics" /><category term="ai" /><category term="software-development" /><summary type="html">A mental model I&apos;ve been thinking about lately comes from Managerial Economics. It&apos;s a framework for analyzing how a firm can trade off capital and labor to produce goods. And I think it&apos;s helpful for Software Developers in this new age of AI.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.germanvelasco.com/images/ai-and-software-dev-from-econ/lower-K-leading-to-higher-Kq-and-Lq.png" /><media:content medium="image" url="https://www.germanvelasco.com/images/ai-and-software-dev-from-econ/lower-K-leading-to-higher-Kq-and-Lq.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Your problem is (often) two problems</title><link href="https://www.germanvelasco.com/blog/your-problem-is-two-problems" rel="alternate" type="text/html" title="Your problem is (often) two problems" /><published>2026-01-26T00:00:00+00:00</published><updated>2026-01-26T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/your-problem-is-two-problems</id><content type="html" xml:base="https://www.germanvelasco.com/blog/your-problem-is-two-problems">&lt;p&gt;Most of my engagements start with a product problem:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“We can’t satisfy demand for features. We need to ship faster.”&lt;/li&gt;
  &lt;li&gt;“Our application is new, but we’re already seeing bugs in the codebase!”&lt;/li&gt;
  &lt;li&gt;“We’re experiencing code quality issues, and we don’t have confidence to change the code.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But fixing the surface-level product-problem isn’t enough. &lt;em&gt;A company has to
change the conditions that led to the issue in the first place.&lt;/em&gt; Otherwise, like
a cancer, the problem grows back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That’s why for most of my engagements I try to solve two problems: the terminal
product-problem and the root team-problem.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A team needs to ship faster. When I join the team, I see pull-request review
takes days to complete. Why? Turns out there’s a trust issue within the team.
So, everyone acts like a gatekeeper.&lt;/li&gt;
  &lt;li&gt;People are seeing bugs in the codebase. And it’s only a year old! Joining the
team, I discover there’s a lot of churn, everyone is new, and there’s a culture
of pushing “hot fixes” and never looking back.&lt;/li&gt;
  &lt;li&gt;People don’t have confidence in changing code. When joining, I realize we have
no test coverage, but people don’t know how to test. We have to talk as a
team and change the culture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, don’t just focus on the surface-level problem. See what’s at the root. Not
only do you fix the problem today, but you also create a
&lt;a href=&quot;/blog/culture-is-a-fifty-day-moving-average&quot;&gt;culture&lt;/a&gt; that compounds the
benefits into the future.&lt;/p&gt;

&lt;p&gt;Remember, your team is the engine behind your product.&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="team" /><category term="culture" /><summary type="html">Most of my clients&apos; problems are two problems: a terminal product-problem and a root team-problem.</summary></entry><entry><title type="html">Why do we write tests?</title><link href="https://www.germanvelasco.com/blog/why-we-test" rel="alternate" type="text/html" title="Why do we write tests?" /><published>2025-06-27T00:00:00+00:00</published><updated>2025-06-27T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/why-we-test</id><content type="html" xml:base="https://www.germanvelasco.com/blog/why-we-test">&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/why-we-write-tests/thumb.jpeg&quot; alt=&quot;Tests, like all other code, should earn the right to exist&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;Tests, like all other code, should earn the right to exist&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;It’s a valid question. Tests, like all other code, are a liability – and
therefore, they should earn their right to exist.&lt;/p&gt;

&lt;p&gt;So, why should we write tests? What’s their raison d’etre?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We write automated tests to continuously verify the behavior of our
 application as early as possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every word in that sentence matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our tests &lt;em&gt;verify&lt;/em&gt; that our application works as we and other stakeholders
expect.&lt;/strong&gt; As professional software developers, we’re responsible for the way the
application works. Our goal should be to build high quality software that serves
the end user. To do that, we need to ensure the application works as it
&lt;em&gt;should&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Of course, any application that survives even a few months of production use
will inevitably &lt;em&gt;change&lt;/em&gt;. As our application grows due to new and shifting
requirements, &lt;strong&gt;we want our tests to continue to verify that everything is still
working as expected. That’s why they should be &lt;em&gt;automated&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But it’s crucial to understand that &lt;strong&gt;we want to verify the &lt;em&gt;behavior of our
application&lt;/em&gt; – not the internal &lt;em&gt;implementation&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We want to ensure that our application as a whole, as well as each module and
function, accomplishes the task that we created it to do. If, instead, we test
how it accomplishes those tasks, we risk coupling our tests to the concrete
implementation, which increases the cost of changing our application – just the
opposite of what we want. That is why it’s often helpful to write the tests
&lt;em&gt;first&lt;/em&gt;, documenting the behavior in executable form before we’ve even written
the implementation.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;we want those tests to run &lt;em&gt;continuously&lt;/em&gt;&lt;/strong&gt;. Each time we change a
function, we should run that function’s tests. When we’re finished working on
that function, we should run the module’s tests. When we finish a feature, we
should run all of our tests. And, of course, before we merge a branch, we should
run all of our tests again. That’s why it’s important that tests run &lt;em&gt;fast&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That’s how we catch regressions before they happen – and &lt;strong&gt;it’s why we want to
verify the behavior of the application &lt;em&gt;as early as possible&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The reality is that everybody tests their applications, regardless of whether or
not they write automated tests:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You can test your application with automated tests (you can even write the
test first!),&lt;/li&gt;
  &lt;li&gt;You can test your application manually before you deploy it – e.g. clicking
on the browser as you develop a feature and once again before deploying to
production to make sure things “look” okay (something that is actually slower,
less repeatable, and therefore more costly!), or&lt;/li&gt;
  &lt;li&gt;You can let your users test your application in production! (here come the
bugs).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of those are different methods of testing your application. They do so at
different points in the development process and at different cost to you.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/why-we-write-tests/cost-of-testing-app.jpg&quot; alt=&quot;Each method of testing happens at a different point in a graph that
compares development process and at different cost levels&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;Each method of testing happens at a different point in time and at
different cost to you!&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;Automated tests happen early, when developers have most context on the code
they’re testing and are therefore the &lt;em&gt;cheapest&lt;/em&gt; way to test your application.
Testing in QA or manually via the browser happens later (and it’s slower), which
increases the turn-around time required to fix any regressions. Finally, having
your users test your application in production happens &lt;em&gt;very late&lt;/em&gt; and is &lt;em&gt;very
costly&lt;/em&gt;. It’s expensive to find the bug and fix it, but it is also costly to
your company’s reputation.&lt;/p&gt;

&lt;p&gt;So, if you want to decrease the cost of testing your application, do it as early
as possible in the development process. Do it when you’re first writing the
code.&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="testing," /><category term="tdd," /><category term="bdd" /><summary type="html">It&apos;s a valid question. Tests, like all other code, are a liability – and therefore, they should earn their right to exist. What&apos;s their reason?</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.germanvelasco.com/images/why-we-write-tests/thumb.jpeg" /><media:content medium="image" url="https://www.germanvelasco.com/images/why-we-write-tests/thumb.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">🤖 ➡️ 😄 Humanize your standups</title><link href="https://www.germanvelasco.com/blog/humanize-your-standups" rel="alternate" type="text/html" title="🤖 ➡️ 😄 Humanize your standups" /><published>2025-05-22T00:00:00+00:00</published><updated>2025-05-22T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/humanize-your-standups</id><content type="html" xml:base="https://www.germanvelasco.com/blog/humanize-your-standups">&lt;p&gt;In the age of remote work (which I love, but I know has trade-offs), standups
can be boring 🥱.&lt;/p&gt;

&lt;p&gt;People are sitting down (not a stand-up anymore, is it?), and it feels like roll
call. When it’s your turn, you just shout what you did yesterday.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/humanize-standups/roll-call.png&quot; alt=&quot;adults bored, and sitting in a class room during roll call&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;You, tomorrow during &apos;standup&apos;&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;That is helpful to some in the team (like PMs), but it can also be done
asynchronously. Which is why, I think, many teams switch to async standups.&lt;/p&gt;

&lt;p&gt;But in a remote world, we’ve lost the coffee machine chats, the “how was your
weekend?” moments, the ping-pong lunch game. We’ve lost some of that connection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I think standups can give us some of that back.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, let’s talk about the work. And while we’re there, let’s focus less on rote
updates and more on blockers and helps – things that lead to collaboration.&lt;/p&gt;

&lt;p&gt;But let’s also humanize ourselves a little. We’re not just 4x4 squares in a
rectangular flatland.&lt;/p&gt;

&lt;p&gt;I like to add levity when I lead standups. I try to be more light-hearted,
self-deprecating, and shout-out non-work things I’ve learned about others (“Did
you know James wrote a book?!”)&lt;/p&gt;

&lt;p&gt;I also like to add quirky elements (like filters and lasers) that help people
loosen up:&lt;/p&gt;

&lt;div class=&quot;flex justify-between&quot;&gt;




&lt;figure&gt;
  &lt;img class=&quot; w-5/6 mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/humanize-standups/wizards.png&quot; alt=&quot;Person with a google meet filter of a wizard&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;Using filters to keep things funny&lt;/legend&gt;
&lt;/figure&gt;





&lt;figure&gt;
  &lt;img class=&quot; w-5/6 mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/humanize-standups/lasers.png&quot; alt=&quot;Person with MacOS lasers in the background&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;Best way to end standup -- with lasers!&lt;/legend&gt;
&lt;/figure&gt;

&lt;/div&gt;

&lt;p&gt;Typically, teams like it, and they adopt it.&lt;/p&gt;

&lt;p&gt;One time, a  teammate running standup acted like a news anchor. They called to
site  correspondents, asking for weather reports and updates from “the field”.
The entire team went along with it. It was hilarious.&lt;/p&gt;

&lt;p&gt;To be clear, I’m not advocating you turn your standup into a 40 minute slog
fest. Keep standups short but add some sweetness too. Doing small things to add
friendliness reminds us that we’re human, in this “thing” together, and creates
camaraderie.&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="teams," /><category term="standups" /><summary type="html">In the age of remote work (which I love, but I know has trade-offs), standups can be boring 🥱. We&apos;ve also lost the coffee machine chats, the &quot;how was your weekend?&quot; moments, the ping-pong lunch game. I think standups can give us some of that camaraderie back.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.germanvelasco.com/images/humanize-standups/roll-call.png" /><media:content medium="image" url="https://www.germanvelasco.com/images/humanize-standups/roll-call.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Elixir Streams hits a 100 videos! 🎉</title><link href="https://www.germanvelasco.com/blog/elixir-streams-hits-a-100-videos" rel="alternate" type="text/html" title="Elixir Streams hits a 100 videos! 🎉" /><published>2024-10-04T00:00:00+00:00</published><updated>2024-10-04T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/elixir-streams-hits-a-100-videos</id><content type="html" xml:base="https://www.germanvelasco.com/blog/elixir-streams-hits-a-100-videos">&lt;p&gt;👋 Hello, hello!&lt;/p&gt;

&lt;p&gt;As of last Tuesday, &lt;a href=&quot;https://www.elixirstreams.com&quot;&gt;Elixir Streams&lt;/a&gt; has a 100 tips!&lt;/p&gt;

&lt;p&gt;Since Elixir Streams has hit this milestone (and I’m amazed it did), I wanted to
look back and share some history, some behind the scenes, and some of my
experiences.&lt;/p&gt;

&lt;h2 id=&quot;when-why-how&quot;&gt;When, why, how&lt;/h2&gt;

&lt;p&gt;I published my &lt;a href=&quot;https://x.com/germsvel/status/1598378996575703045&quot;&gt;first tip&lt;/a&gt; on Twitter on December 1st, 2022.&lt;/p&gt;

&lt;p&gt;A few days before, I had seen someone retweet a video from Wes Bos. I liked the
format of explaining a concept in a short video. So, when I was about to share
the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--info&lt;/code&gt; flag for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mix phx.routes&lt;/code&gt;, I thought, “why don’t I make a video
about it instead?”&lt;/p&gt;

&lt;p&gt;I recorded the video and threw it out there without further thought.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/first-tip.png&quot; alt=&quot;The first tweet showing the first tip video&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;My first tweet with a video tip&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;Since I had created a video, I thought, “why not put it in YouTube too?” So, a
few days later &lt;a href=&quot;https://youtu.be/fm5H2ZU7h2g&quot;&gt;I did&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I had already done recordings for my &lt;a href=&quot;https://www.testingliveview.com/&quot;&gt;Testing LiveView&lt;/a&gt; course, so I already had
some familiarity with recording and editing videos. But as you can see, my first
tips were a bit rough – I didn’t even know that Twitter’s videos were supposed
to be square and didn’t know what YouTube shorts were at all.&lt;/p&gt;

&lt;p&gt;Honestly, I didn’t think much about it. I didn’t have a grand plan to keep doing
them. It was just a spur-of-the-moment thing… so, naturally, it stuck.&lt;/p&gt;

&lt;h2 id=&quot;the-website&quot;&gt;The website&lt;/h2&gt;

&lt;p&gt;After publishing a good number of tips in Twitter and YouTube, I figured I would
continue publishing them.&lt;/p&gt;

&lt;p&gt;But the videos were spread across Twitter, YouTube (and even LinkedIn). I wanted
to have a single website where I could put all the videos, and maybe even a
newsletter sign up so people could get notified when I published them.&lt;/p&gt;

&lt;p&gt;Thus, in June 2023 the &lt;a href=&quot;https://www.elixirstreams.com&quot;&gt;elixirstreams.com&lt;/a&gt; website was born.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/elixir-streams-website.png&quot; alt=&quot;A screenshot of the Elixir Strems website&apos;s homepage&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;Elixir Streams homepage now&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;One huge benefit of having the YouTube videos on the website is that YouTube
doesn’t show ads when the videos are embedded! 🥳&lt;/p&gt;

&lt;p&gt;I never liked that YouTube automatically included ads in my videos that I
couldn’t turn off. I’m not in their ad program, so I can’t reject them. So, at
first, I didn’t want to send people to YouTube since they’d get ads. But, I feel
perfectly comfortable sending people to the Elixir Streams website. No ads.&lt;/p&gt;

&lt;h2 id=&quot;the-website-tech&quot;&gt;The website tech&lt;/h2&gt;

&lt;p&gt;The website has never used fancy tech. It’s just another static site in my
collection.&lt;/p&gt;

&lt;p&gt;I chose to do a static site since:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;(a) I don’t really need dynamic pages, and&lt;/li&gt;
  &lt;li&gt;(b) I can host the website for free with Render.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the time, I really hoped there would be good Elixir static site generator,
but I couldn’t find one. I still don’t think we have a mature, well-used, and
well-maintained one. I might be wrong, but that was my impression at the time
after doing a little bit of research.&lt;/p&gt;

&lt;p&gt;I thought about building one… it would be awesome. But I was trying to publish
videos, not build a static site generator. So, with a great deal of effort, I
&lt;em&gt;resisted&lt;/em&gt; the yack shave. Thankfully, I’d used Jekyll and Gatsby before, so I
could pick one of them.&lt;/p&gt;

&lt;p&gt;With Jekyll (that’s what this website you’re reading is built with), there’s
&lt;em&gt;some&lt;/em&gt; pain when setting up. But once I have the website running, it’s usually
pretty smooth sailing.&lt;/p&gt;

&lt;p&gt;Gatsby, on the other side (that’s what my &lt;a href=&quot;https://www.tddphoenix.com/&quot;&gt;TDD Phoenix&lt;/a&gt; site is built with) has
always a huuuuge pain. It’s probably a phenomenal static site generator, but for
the amount of time I wanted to spend configuring it, I’ve always found it a
royal pain to go back and do anything with my TDD Phoenix website.&lt;/p&gt;

&lt;p&gt;So, I decided &lt;em&gt;against&lt;/em&gt; Gatsby and went with Jekyll.&lt;/p&gt;

&lt;h2 id=&quot;the-recording-tech&quot;&gt;The recording tech&lt;/h2&gt;

&lt;p&gt;I’ve upgraded my tech over time, but probably not as much as I should’ve. But
hey, I’m doing this for free! 😅&lt;/p&gt;

&lt;p&gt;I upgraded whatever Logitech camera I had to an Elgato Facecam Pro. I also
bought a better source of light (an Elgato ring light) – which made me look
like a corpse, until I discovered (very recently, to my shame) that you can
adjust the temperature of the light.&lt;/p&gt;

&lt;p&gt;I’ve always used Screenflow to edit videos. I’m happy with it. And I’ve always
used an Audio Technica ATR2100 mic. A former colleague of mine who is a video
editor (👋 hi Thom) recommended them to me back in 2019. I remember him saying
something like this, “that’s a good, simple (read USB), cheaper mic for spaces
that aren’t sound proofed very well” – just what I needed. And I’ve never
needed anything more.&lt;/p&gt;

&lt;p&gt;I’m hoping to improve my video more. I never really wanted my face to be
prominent in the video tips, but it’s definitely part of it.&lt;/p&gt;

&lt;p&gt;And recently, I got a rude awakening. I started &lt;a href=&quot;https://elixirfriends.transistor.fm/&quot;&gt;a podcast&lt;/a&gt; that also goes on
YouTube, and Peter, my first Elixir friend, had an awesome setup. Mine &lt;em&gt;paled&lt;/em&gt;
in comparison.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/dead-interview-the-living.png&quot; alt=&quot;A screenshot of me and Peter talking in a podcast. I look pale. Peter looks great&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;When the dead chat with the living&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;So, I’ve taken some steps already – like realizing that you can change the ring
light’s temperature, adjusting the camera’s temperature, improving my light in
the background, and actually recording my video in 4k. We’ll see how that goes.&lt;/p&gt;

&lt;p&gt;Here’s a screenshot of my face in an upcoming tip (video tip 101!):&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/better-light.png&quot; alt=&quot;A screenshot of me with better camera settings and light&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;Better camera settings, warmer light, and proper face alignment&lt;/legend&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;the-intro-and-outro&quot;&gt;The intro and outro&lt;/h2&gt;

&lt;p&gt;The first few times I recorded, I didn’t know how to start talking. I said
things like “Hey there” or “Good morning”. But nothing felt great.&lt;/p&gt;

&lt;p&gt;It’s surprising how weird it feels to stare at a 3…2…1 countdown on your
screen and then go directly into speaking like you’ve been talking all along. At
least, it’s weird for me.&lt;/p&gt;

&lt;p&gt;In any case, after a few videos, I started a video with “hello, hello”. And that
seemed okay. Unfortunately, it became a crutch! I can’t start videos anymore
&lt;em&gt;without&lt;/em&gt; saying “hello, hello”. It’s the cue my brain uses to go into recording
mode. 🤦&lt;/p&gt;

&lt;p&gt;Fortunately, people seem to like it. It’s even become something of my signature!&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/hello-hello-comment.png&quot; alt=&quot;A YouTube comment saying &apos;Hello, hello&apos;&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;People seem to like the &apos;Hello, hello!&apos;&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;The one thing I wanted to have was a cool outro. I always remember Avdi Grimm’s
“happy hacking!” ending to his videos. And if it weren’t so darn cool, I’d steal
it for myself. But nah, I can’t pull that off.&lt;/p&gt;

&lt;p&gt;Thankfully, my outro of “I hope you like it” came naturally at some point. And I
kinda like it. It even gets people to reply to it sometimes. 😂&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/i-do-like-it-reply.png&quot; alt=&quot;A YouTube comment saying &apos;I do like it&apos;&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;I like that people like it&lt;/legend&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;the-reception&quot;&gt;The reception&lt;/h2&gt;

&lt;p&gt;The Elixir community’s reception to the videos has been great!&lt;/p&gt;

&lt;p&gt;I have been amazed at how many people reply saying that they really appreciate
the videos, how it helps them, and how they love the format – short and to the
point so they can go on with their day.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/elixir-streams-testimonials.png&quot; alt=&quot;Several testimonials in the Elixir Streams website about how much they like the videos&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;Some of the YouTube comments I copied to the Elixir Streams website&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;By far, the coolest comment anybody’s said about my video tips was [Bryan’s tweet]:&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/bryan-awesome-tweet.png&quot; alt=&quot;Bryan tweeting: &apos;Hands down the most nutrient-dense Elixir diet per second of time invested&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;Every word is gold. Bryan is a wordsmith&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;It’s such a nice comment that I made it the top quote on the Elixir Streams
website.&lt;/p&gt;

&lt;p&gt;The tips also get mentioned frequently in the &lt;a href=&quot;https://podcast.thinkingelixir.com/&quot;&gt;Thinking Elixir podcast&lt;/a&gt;. It’s
always such a nice surprise to be listening to the news and hear Mark and David
talk about one of the tips!&lt;/p&gt;

&lt;p&gt;But the biggest surprise to me came when I attended ElixirConf 2023 and 2024.
People started approaching me and saying, “You’re the video guy”, or “I like
your videos”, or they just went straight into “hello, hello!”&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/elixir-conf-with-dhony.png&quot; alt=&quot;An tweet posted by Dhony of an image of me and him at ElixirConf with the words Hello! Hello!&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;I guess I&apos;m just the &apos;Hello! Hello!&apos; guy now. (Hi Dhony!)&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;I also love when people say the videos have helped them. A few people have
reached out in Slack or YouTube over the years to tell me how much they’ve
helped them. That’s honestly one of the coolest things.&lt;/p&gt;

&lt;p&gt;Isn’t the internet amazing? People from all over the world can create things,
share them, and others find them and improve through them!&lt;/p&gt;

&lt;p&gt;It’s been a wonderful experience being able to contribute to the community in
such a visible way. I’d always been more of a writer (hello blog post!), but
people really seem to like the short video format and benefit from it. So, I’m
glad the community received them so well.&lt;/p&gt;

&lt;h2 id=&quot;the-metrics&quot;&gt;The metrics&lt;/h2&gt;

&lt;p&gt;I don’t have a canonical source of metrics. I have YouTube metrics (which are
easy to get), the Elixir Streams website metrics (I use Fathom with as little
tracking as possible), and Twitter metrics (which are kinda useless).&lt;/p&gt;

&lt;p&gt;I’m not exactly sure what counts as a “view” in YouTube shorts. Many of the tips
are under a minute, so they automatically get dropped into that bucket. And I
imagine many people scroll away pretty quickly. So, who knows if that’s a view
or not.&lt;/p&gt;

&lt;p&gt;The Elixir Streams website’s metrics &lt;em&gt;should&lt;/em&gt; in theory be included in YouTube’s
metrics (since that’s where the videos are hosted). So, I imagine the YouTube
metrics are the upper bound and the Elixir Streams metrics are the lower bound.
But I’m not sure how accurate they are.&lt;/p&gt;

&lt;p&gt;First, the website, which is the lower bound.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/elixir-streams-stats.png&quot; alt=&quot;30.4k people and 48.6k views&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;Fathom analytics for visits to Elixir Streams&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;Since the creation of the website, the website’s had 30.4k people and 48.6k
views. I don’t have anything to compare that number to, but that doesn’t seem
bad. If we round to 50k views and we have 100 videos, that’s roughly 500 views
per video. That doesn’t count people going in through YouTube, or people seeing
the native Twitter videos. Again, just a rough estimate.&lt;/p&gt;

&lt;p&gt;YouTube stats are larger. I have one video that isn’t Elixir Streams related, so
I filtered that out.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;w-2/3  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/youtube-stats.png&quot; alt=&quot;YouTube stats: 90.3k views. 1.5k watch time&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;YouTube analytics since I posted first tip&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;In total, it says the videos had 90.3k views. That’s almost double, and
therefore more like 1k views per video. Not bad! YouTube also shows 1.5k hours
of watch time. That kinda blows my mind. People have spent over a thousand hours
watching my 1-3 min videos.&lt;/p&gt;

&lt;p&gt;By the way, if you look closely, you’ll see a spike near the beginning. That’s
around December 30th when a &lt;a href=&quot;/blog/phoenix-1-7-is-view-less&quot;&gt;blog post&lt;/a&gt; I wrote made it to the top of hackernews
– the post had the video embedded in it.&lt;/p&gt;

&lt;p&gt;Twitter doesn’t have aggregate metrics for all my video posts. I have aggregate
metrics for all my tweets, but that’s a lot of noise.&lt;/p&gt;

&lt;p&gt;So, instead of showing the aggregate, here’s a sample of a couple of recent
posts.&lt;/p&gt;

&lt;p&gt;First one on the lower end:&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/unquote-splicing-tweet.png&quot; alt=&quot;Metrics for unquote splicing post&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;A recent post that wasn&apos;t really a hit&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;It got 1.5k impressions (whatever that means), 15 likes, 1 reply, and 3 reposts.&lt;/p&gt;

&lt;p&gt;And here’s a more popular one:&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;  mx-auto rounded-md&quot; src=&quot;https://www.germanvelasco.com/images/elixir-streams-100/timer-tweet.png&quot; alt=&quot;Metrics for :timer helpers post&quot; /&gt;
  &lt;legend class=&quot;text-sm text-center sm:-mt-6&quot;&gt;A recent post that did pretty well&lt;/legend&gt;
&lt;/figure&gt;

&lt;p&gt;The video got 7k impressions, 62 likes, 2 replies, and 7 reposts.&lt;/p&gt;

&lt;p&gt;This view of Twitter’s analytics doesn’t show how many bookmarked it, which is a
shame. I think that’s a good metric of whether people like the post too.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h2&gt;

&lt;p&gt;I’ve considered what I want Elixir Streams to become. But I confess I’m not sure
yet.&lt;/p&gt;

&lt;p&gt;One one hand, I thought it could be my hub for Elixir tips and courses. I like
the idea, but that would mean investing more in courses. I think it could be a
good strategy, but I’m unsure if I want to create a ton of courses (they take a
lot of time).&lt;/p&gt;

&lt;p&gt;I’ve also considered making it a community place where I just curate YouTube
videos from the Elixir community. It’s still something I’m considering, but
there’s no going back if I announce that. So, I haven’t pulled the trigger with
that yet.&lt;/p&gt;

&lt;p&gt;Finally, I considered taking sponsorships to help offset the cost (of my time)
of recording videos. Maybe I could even make it more of my regular job. But I’m
not sure if I want to do that either. Sometimes when I tie things to “need to
make money”, they lose the pure joy of creation. And if I do decide to try to
make money somehow, I think I’d rather go the courses route.&lt;/p&gt;

&lt;p&gt;In the end, it may just remain what is is. A hub for free video tips I create.
And hopefully a great resource for the community.&lt;/p&gt;

&lt;h2 id=&quot;thank-you-for-watching&quot;&gt;Thank you for watching!&lt;/h2&gt;

&lt;p&gt;Finally, I’d like to say thank you to all the people who’ve watched the videos!&lt;/p&gt;

&lt;p&gt;Without people watching and mentioning how much they like them, I would’ve
stopped doing them long ago.&lt;/p&gt;

&lt;p&gt;Hope you like ‘em 😉&lt;/p&gt;

&lt;p&gt;– German&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="elixir-streams," /><category term="videos," /><category term="elixir," /><category term="phoenix" /><summary type="html">As of last Tuesday, Elixir Streams has a 100 tips! Since Elixir Streams has hit this milestone (and I&apos;m amazed it did), I wanted to look back and share some history, some behind the scenes, and some of my experiences.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.germanvelasco.com/images/elixir-streams-100/elixir-streams-website-quote.png" /><media:content medium="image" url="https://www.germanvelasco.com/images/elixir-streams-100/elixir-streams-website-quote.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Introducing PhoenixTest</title><link href="https://www.germanvelasco.com/blog/introducing-phoenix-test" rel="alternate" type="text/html" title="Introducing PhoenixTest" /><published>2024-01-31T00:00:00+00:00</published><updated>2024-01-31T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/introducing-phoenix-test</id><content type="html" xml:base="https://www.germanvelasco.com/blog/introducing-phoenix-test">&lt;p&gt;&lt;a href=&quot;https://hexdocs.pm/phoenix_test&quot;&gt;PhoenixTest&lt;/a&gt; provides a unified way of writing feature tests – regardless of
whether you’re testing LiveView pages or static pages.&lt;/p&gt;

&lt;p&gt;It also handles navigation between LiveView and static pages seamlessly. You
don’t have to worry about what type of page you’re visiting or navigating to.
So, you can test a flow going from static to LiveView pages and back without
having to worry about the underlying implementation.&lt;/p&gt;

&lt;p&gt;Just write your tests from the user’s perspective. ✨&lt;/p&gt;

&lt;h2 id=&quot;why-create-phoenixtest&quot;&gt;Why create PhoenixTest?&lt;/h2&gt;

&lt;p&gt;With the advent of LiveView, I find myself writing less and less JavaScript.&lt;/p&gt;

&lt;p&gt;Sure, there are sprinkles of it here and there, and there’s always the
occasional need for something more.&lt;/p&gt;

&lt;p&gt;But for the most part, if I’m going to build a page that needs interactivity, I
use LiveView. If I’m going to write a static page, I use regular controllers +
views/HTML modules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem is that LiveView pages and static pages have &lt;em&gt;vastly different&lt;/em&gt;
testing strategies.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If I use LiveView, I can write my tests like this:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;live&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;~p&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#greet-guest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello, guest!&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But if I want to test a static page, we have to resort to controller testing:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;~p&quot;/greet_page&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;html_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello, guest!&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That means I don’t have ways of interacting with static pages at all!&lt;/p&gt;

&lt;p&gt;What if I want to submit a form or click a link? And what if a click takes me
from a LiveView to a static view or vice versa?&lt;/p&gt;

&lt;h3 id=&quot;a-unified-way-of-writing-feature-tests&quot;&gt;A Unified way of writing feature tests&lt;/h3&gt;

&lt;p&gt;So, I wanted a unified way of writing feature tests – regardless of whether or
not you’re testing LiveView pages or static pages.&lt;/p&gt;

&lt;p&gt;And that’s what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PhoenixTest&lt;/code&gt; does!&lt;/p&gt;

&lt;p&gt;Imagine having a workflow that starts on a static page. We click a link and are
taken to a LiveView page. Then, we fill a LiveView form (which could even have
its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phx-change&lt;/code&gt; triggered), and then we submit the form. Finally, we assert
that what we expect to see is present.&lt;/p&gt;

&lt;p&gt;All of that in a succinct, pipe-friendly way!&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;can create a user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;conn:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# &amp;lt;- could be LiveView or static page&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;click_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;- navigate between LiveViews and static pages&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#user-form&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Aragorn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;email:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aragorn@dunedain.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ^ will trigger `phx-change` if present&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;click_button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Create&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;- submits LiveView forms and regular forms&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assert_has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Aragorn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;- provides better error messages&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;improving-assertions&quot;&gt;Improving assertions&lt;/h3&gt;

&lt;p&gt;Then there’s the problem of assertions.&lt;/p&gt;

&lt;p&gt;Because LiveView and controller tests use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=~&lt;/code&gt; for assertions, the error
messages aren’t very helpful when assertions fail.&lt;/p&gt;

&lt;p&gt;After all, we’re just comparing two blobs of text – and trust me, HTML pages
can get very large and hard to read as a blob of text in your terminal.&lt;/p&gt;

&lt;p&gt;LiveView tries to help with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_element?/3&lt;/code&gt; helper, which allows us to
target elements by CSS selectors and text.&lt;/p&gt;

&lt;p&gt;But, unfortunately, it still doesn’t provide the best errors.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_element?/3&lt;/code&gt; only tells us what was passed into the function. It doesn’t
give us a clue as to what else might’ve been on the page – maybe we just made a
small typo and we have no idea!&lt;/p&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PhoenixTest.assert_has/3&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PhoenixTest.refute_has/3&lt;/code&gt;, we can provide
better errors when assertions fail.&lt;/p&gt;

&lt;h3 id=&quot;how-did-you-write-feature-tests-before&quot;&gt;How did you write feature tests before?&lt;/h3&gt;

&lt;p&gt;In the past, I would’ve used &lt;a href=&quot;https://hexdocs.pm/wallaby/readme.html&quot;&gt;Wallaby&lt;/a&gt; to write feature tests.&lt;/p&gt;

&lt;p&gt;But since I’m not writing as much JavaScript, I don’t feel the need for
something so heavy – requiring chromedriver and slowing down our tests.&lt;/p&gt;

&lt;p&gt;Instead, I’d like to have something more akin to what LiveView tests brought
us – tests that act as though there’s a driver, but they mostly parse HTML and
allows us to make assertions about it.&lt;/p&gt;

&lt;p&gt;That’s where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PhoenixTest&lt;/code&gt; shines.&lt;/p&gt;

&lt;p&gt;Of course, without something like chromedriver, we cannot test JavaScript. So,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PhoenixTest&lt;/code&gt; remains blissfully ignorant of it. If you need that, check
Wallaby.&lt;/p&gt;

&lt;p&gt;But if you’re looking for something to write fast feature tests for your
LiveView pages and static pages – and something that is (hopefully) a pleasure
to write – check out &lt;a href=&quot;https://hexdocs.pm/phoenix_test&quot;&gt;PhoenixTest&lt;/a&gt;!&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="phoenix," /><category term="live-view," /><category term="ecto," /><category term="elixir" /><summary type="html">PhoenixTest provides a unified way of writing feature tests -- regardless of whether you&apos;re testing LiveView pages or static pages.</summary></entry><entry><title type="html">Don’t silo your team</title><link href="https://www.germanvelasco.com/blog/dont-silo-your-team" rel="alternate" type="text/html" title="Don’t silo your team" /><published>2023-11-06T00:00:00+00:00</published><updated>2023-11-06T00:00:00+00:00</updated><id>https://www.germanvelasco.com/blog/dont-silo-your-team</id><content type="html" xml:base="https://www.germanvelasco.com/blog/dont-silo-your-team">&lt;p&gt;I have worked in teams where a product manager (or someone equivalent) assigns
tasks to developers. On paper, that works. And it’s likely done in the name of
parallelizing work – &lt;em&gt;“look at all the projects we’re working on at once!”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But in my experience, that top-down approach to assigning work has a huge
downside: it discourages team collaboration.&lt;/p&gt;

&lt;p&gt;When a product manager assigns tasks, every developer feels responsible for
“their“ amount of work – especially in a company that rewards developers who
finish more tasks. And that destroys collaboration.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;not-prose rounded p-4 w-3/5&quot; src=&quot;/images/dont-silo-team/dev-working-alone-in-cubicle.png&quot; alt=&quot;Sad software developer, sitting alone in a cubicle&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I often see that lack of collaboration play out in code review.&lt;/p&gt;

&lt;p&gt;When developers are measured by the number of tasks they complete, and when
those tasks are assigned by a product manager, you’ll quickly find that
developers aren’t doing as much code review as needed. Everyone wants &lt;em&gt;their&lt;/em&gt;
pull requests to be reviewed, but no one wants to do the reviewing. So, pull
requests remain open for longer than necessary, and you’ll probably hear about
it in retro.&lt;/p&gt;

&lt;p&gt;Your team is divided because every developer is out for themselves. And of
course, it’s not ill intended (or at least, it may not be). But your team’s
incentives are working against collaboration.&lt;/p&gt;

&lt;p&gt;By contrast, the best teams I’ve worked with see programming as a team sport.
They share ownership of the codebase. And they see the team’s progress – not
individual progress – the goal to be attained.&lt;/p&gt;

&lt;p&gt;In those teams, tasks aren’t assigned. No developer is responsible for a task
unless they take it upon themselves. And who better to know which task each one
should take?&lt;/p&gt;

&lt;p&gt;Many times, a task still lands on a single developer. Other times two developers
choose to pair on a task. And yet other times, several developers collaborate
to solve the difficult parts together but then separate to parallelize menial
tasks.&lt;/p&gt;

&lt;p&gt;A product manager can help list the tasks in terms of priority: the most
important task is at the top, then the next, then the next. That is essential
information for the team to have the most impact on the business.&lt;/p&gt;

&lt;p&gt;But developers don’t need to be assigned each individual task – filing in line
to receive their orders. Instead, trust the developers to work together to
complete the tasks needed. That encourages the team to work on the solutions in
whatever way they see fit.&lt;/p&gt;

&lt;p&gt;When you do that, you will find that people are more comfortable doing code
review because they’re treating progress as a team sport – the &lt;em&gt;team&lt;/em&gt; is trying
to finish the tasks, not each developer their own.&lt;/p&gt;

&lt;p&gt;Of course, &lt;em&gt;not&lt;/em&gt; assigning tasks to people won’t automatically make your team
great at collaboration. There are other things needed – good communication, a
willingness to work together, etc. But at the very least, you’re not hindering
your team by creating a structure that actively works against collaboration.&lt;/p&gt;</content><author><name>German Velasco</name></author><category term="blog" /><category term="team," /><category term="collaboration," /><category term="pair-programming" /><summary type="html">I have worked in teams where a product manager assigns tasks to developers. On paper, that works. And it&apos;s likely done in the name of parallelizing work -- &quot;look at all the projects we&apos;re working on at once!&quot; But in my experience, that top-down approach to assigning work has a huge downside: it discourages team collaboration.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.germanvelasco.com/images/dont-silo-team/dev-working-alone-in-cubicle.png" /><media:content medium="image" url="https://www.germanvelasco.com/images/dont-silo-team/dev-working-alone-in-cubicle.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>