<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>Marcel Fahle</title>
	<subtitle>Always be learning.</subtitle>
	
	<link href="https://marcelfahle.net/feed/feed.xml" rel="self"/>
	<link href="https://marcelfahle.net"/>
	<updated>2025-07-13T00:00:00+00:00</updated>
	<id>https://marcelfahle.net</id>
	<author>
		<name>Marcel Fahle</name>
		<email></email>
	</author>
	
	
	<entry>
		<title>Video Uploads with Phoenix LiveView and Mux</title>
		<link href="https://marcelfahle.net/posts/2022-12-17-mux-uploads/"/>
		<updated>2022-12-17T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/2022-12-17-mux-uploads/</id>
		<content type="html">&lt;p&gt;Uploading large video files is something that I often do when I work on Bold. But even if you rely on an incredible API like Mux (which we do) or use all the shortcuts that Phoenix and LiveView provide, getting everything set up correctly is not always trivial.&lt;/p&gt;
&lt;p&gt;So let&#39;s walk through a complete example using Phoenix LiveView and Mux from start to finish.&lt;/p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup &lt;a class=&quot;direct-link&quot; href=&quot;#setup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We start by creating a new phoenix app (we&#39;re using Phoenix 1.7-rc in this example):&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;mix phx.new watermarkr &lt;span class=&quot;token parameter variable&quot;&gt;--live&lt;/span&gt; --no-dashboard --binary-id &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; watermarkr&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: We&#39;re using &lt;code&gt;binary_id&lt;/code&gt; as our primary key type because it makes it later easier to tie our own video records to the assets from Mux.&lt;/p&gt;
&lt;p&gt;Next, we&#39;ll add a video resource and use the Phoenix generators to keep it simple. For now, we&#39;ll have a title and the mux asset and playback IDs to access our content and later play even it:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;mix phx.gen.live Media Video videos title:string asset_id:string playback_id:string&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s grab the routes from the prompt and add them to our router:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/router.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;scope &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;WatermarkrWeb&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    pipe_through &lt;span class=&quot;token atom symbol&quot;&gt;:browser&lt;/span&gt;&lt;br&gt;&lt;br&gt;    get &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;PageController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:home&lt;/span&gt;&lt;br&gt;&lt;br&gt;    live &lt;span class=&quot;token string&quot;&gt;&quot;/videos&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;VideoLive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:index&lt;/span&gt;&lt;br&gt;    live &lt;span class=&quot;token string&quot;&gt;&quot;/videos/new&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;VideoLive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:new&lt;/span&gt;&lt;br&gt;    live &lt;span class=&quot;token string&quot;&gt;&quot;/videos/:id/edit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;VideoLive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:edit&lt;/span&gt;&lt;br&gt;&lt;br&gt;    live &lt;span class=&quot;token string&quot;&gt;&quot;/videos/:id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;VideoLive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:show&lt;/span&gt;&lt;br&gt;    live &lt;span class=&quot;token string&quot;&gt;&quot;/videos/:id/show/edit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;VideoLive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:edit&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to create our Video IDs manually because we already need one before saving our video resource, so we can tie it to the Mux Upload.&lt;/p&gt;
&lt;p&gt;So, let&#39;s also turn off the autogeneration of IDs on the database level and, instead, make sure we&#39;re able to add one manually by adding it to the &lt;code&gt;cast&lt;/code&gt; and &lt;code&gt;validate_required&lt;/code&gt; function. And while we&#39;re here, we can also quickly remove the asset and playback_ids from our changeset validation so that those won&#39;t trouble us later during our client-side form validation (we&#39;ll set those via Mux webhook later):&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermark/media/video.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Watermarkr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Video&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Ecto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Schema&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Ecto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Changeset&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token attribute variable&quot;&gt;@primary_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:binary_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autogenerate:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attribute variable&quot;&gt;@foreign_key_type&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:binary_id&lt;/span&gt;&lt;br&gt;&lt;br&gt;  schema &lt;span class=&quot;token string&quot;&gt;&quot;videos&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    field &lt;span class=&quot;token atom symbol&quot;&gt;:asset_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:string&lt;/span&gt;&lt;br&gt;    field &lt;span class=&quot;token atom symbol&quot;&gt;:playback_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:string&lt;/span&gt;&lt;br&gt;    field &lt;span class=&quot;token atom symbol&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:string&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;timestamps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token attribute variable&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attrs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    video&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;attrs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:asset_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:playback_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validate_required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it&#39;s time to create our ID. In our LiveView, let&#39;s generate one as soon as the User hits the &amp;quot;New&amp;quot; button:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/live/video_live/index.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    socket&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:page_title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;New Video&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Ecto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We must also ensure that our manually generated ID gets passed down to the &lt;code&gt;Media.create_video/1&lt;/code&gt; function. We can add it to the video_params inside the &lt;code&gt;save_video/3&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/live/video_live/index.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;save_video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; video_params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  video_params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video_params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create_video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video_params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterward, we create our database and run our migration (make sure your database connection is configured correctly in &lt;code&gt;config/dev.exs&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;mix ecto.create &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ecto.migrate&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you start the dev server with &lt;code&gt;mix phx.server&lt;/code&gt; and navigate to &lt;a href=&quot;http://localhost:4000/videos&quot;&gt;http://localhost:4000/videos&lt;/a&gt;, you&#39;ll see that we now have a mighty fine UI available for our video resource (Thanks, Team Tailwind!)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marcelfahle.net/img/videoupload-screen1.png&quot; alt=&quot;Our first app&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;file-upload&quot;&gt;File upload &lt;a class=&quot;direct-link&quot; href=&quot;#file-upload&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s tackle the video uploads next.&lt;br&gt;
We start by adding the Mux API Wrapper to our dependencies.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# mix.exs&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; deps &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# [...]&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;~&gt; 2.5.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now add the Mux Access Token ID and Secret Key, which you can grab from your Mux settings page, to our config/dev.exs and run mix deps.get afterward:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# config/dev.exs&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;config &lt;span class=&quot;token atom symbol&quot;&gt;:mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;  &lt;span class=&quot;token attr-name&quot;&gt;access_token_id:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MUX_TOKEN_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;  &lt;span class=&quot;token attr-name&quot;&gt;access_token_secret:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MUX_TOKEN_SECRET&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The way these kinds of uploads work is basically, once a user initiates an upload, we&#39;re requesting a signed upload URL from Mux, which is unique to that current upload. This URL is then handed over to the JavaScript client (we use UpChunk), which takes care of the actual uploading process for us.&lt;/p&gt;
&lt;p&gt;We&#39;ll plug the uploader directly into our create video modal so that the actual upload only happens once the User clicks &amp;quot;Save Video&amp;quot; and submits the form.&lt;/p&gt;
&lt;p&gt;Let&#39;s start by accepting uploads for our video resource and let our LiveView request the signed upload URL. To do that, we need a few things:&lt;/p&gt;
&lt;p&gt;First, we&#39;ll configure our uploader inside our video form&#39;s &lt;a href=&quot;https://hexdocs.pm/phoenix_live_view/Phoenix.LiveComponent.html#c:update/2&quot;&gt;update/2&lt;/a&gt; callback using &lt;a href=&quot;https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#allow_upload/3&quot;&gt;Phoenix.LiveView.allow_upload/3&lt;/a&gt;. We&#39;ll pass it a presign_upload/2 function to request our (external) signed URL from Mux. We&#39;ll write that function in a little bit:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/live/video_live/form_component.ex&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attribute variable&quot;&gt;@impl&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;video:&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; assigns&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    changeset &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;change_video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      socket&lt;br&gt;      &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;allow_upload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:video_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token attr-name&quot;&gt;accept:&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token attr-name&quot;&gt;max_file_size:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100_000_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token attr-name&quot;&gt;external:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;presign_upload&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; changeset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will give us an uploads assign on our socket connection, which lets us render our file input field. Let&#39;s add that to our video form now:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;br&gt;# lib/watermarkr_web/live/video_live/form_component.ex&lt;br&gt;# [...]&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;.input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{{f,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;:title}}&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;.input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{{f,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;:has_watermark}}&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;has_watermark&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;.live_file_input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;upload&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{@uploads.video_file}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://marcelfahle.net/img/videoupload-screen2.png&quot; alt=&quot;Upload Field&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once we hit the submit button, LiveView will process all the configured uploads in our form before invoking the &lt;code&gt;handle_event/3&lt;/code&gt; callback for submitting the form.&lt;/p&gt;
&lt;p&gt;That means we need to take care of the actual uploading next. We will use the &lt;code&gt;presign_upload/2&lt;/code&gt; function to request a signed URL from Mux to begin uploading. We will also add our own video ID, which we created earlier, as a passthrough value, so we can link the Mux asset to our video resource once encoding is complete.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/live/video_live/form_component.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;presign_upload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_entry&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    video &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;video&lt;br&gt;&lt;br&gt;    params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;new_asset_settings&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;passthrough&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;playback_policies&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;cors_origin&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;url&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _client&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Uploads&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;uploader:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UpChunk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;endpoint:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Signed URL in hand, we&#39;ll stuff that together with the information which uploader to use into a map (&lt;code&gt;%{uploader: &amp;quot;UpChunk&amp;quot;, endpoint: url}&lt;/code&gt;), which LiveView will then hand over to JavaScript. Let&#39;s tackle that part next.&lt;br&gt;
First, we need to add UpChunk to our JS dependencies:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; assets &lt;span class=&quot;token parameter variable&quot;&gt;--save&lt;/span&gt; @mux/UpChunk&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we&#39;ll add the uploader function to our JS:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// assets/js/app.js&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; UpChunk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@mux/upchunk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; uploaders &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;uploaders&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;UpChunk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onViewError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;// create upchunk upload with signed url (endpoint)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;// and file object received from liveview&lt;/span&gt;&lt;br&gt;  entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; endpoint &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; upload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; UpChunk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createUpload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; endpoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// stop upload on error and report back&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// to liveview&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;onViewError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; upload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    upload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;detail&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// report progress and success back to liveview&lt;/span&gt;&lt;br&gt;    upload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;progress&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;detail &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;progress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;detail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    upload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;progress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; liveSocket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LiveSocket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/live&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Socket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  uploaders&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;_csrf_token&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; csrfToken&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;upload-progress&quot;&gt;Upload Progress &lt;a class=&quot;direct-link&quot; href=&quot;#upload-progress&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the code above, you&#39;ll see a few functions being called on the &lt;code&gt;entry&lt;/code&gt; object (&lt;code&gt;progress()&lt;/code&gt;, &lt;code&gt;error()&lt;/code&gt;). Those are the callbacks our LiveView form provides to communicate back to it. Let&#39;s use that information to show the upload progress. We&#39;ll do that by adding a little progress bar to our markup, and I&#39;ve added mine right under the &lt;a href=&quot;https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#live_file_input/1&quot;&gt;live_file_input&lt;/a&gt; component:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;// lib/watermarkr_web/live/video_live/form_component.ex&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;:for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{entry&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@uploads.video_file.entries}&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;w-full bg-gray-200 rounded-full h-2.5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &amp;lt;div class=&quot;bg-blue-600 h-2.5 rounded-full&quot; style={&quot;width: #{entry.progress}%&quot;}&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Easy peasy.&lt;/p&gt;
&lt;h2 id=&quot;webhooks&quot;&gt;Webhooks &lt;a class=&quot;direct-link&quot; href=&quot;#webhooks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The last missing piece to the puzzle is to wait for the Mux encoder to do its job processing our uploads. Thankfully, Mux will notify us via webhooks when everything is done, and all we need to do is to wait and listen for those webhooks.&lt;/p&gt;
&lt;p&gt;We&#39;ll start by adding an API route to our router, which Mux can then trigger with status updates:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/router.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;scope &lt;span class=&quot;token string&quot;&gt;&quot;/api&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;WatermarkrWeb&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  pipe_through &lt;span class=&quot;token atom symbol&quot;&gt;:api&lt;/span&gt;&lt;br&gt;&lt;br&gt;  post &lt;span class=&quot;token string&quot;&gt;&quot;/webhooks/mux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;WebhookController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:mux&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;webhooks-on-localhost%3A&quot;&gt;Webhooks on localhost: &lt;a class=&quot;direct-link&quot; href=&quot;#webhooks-on-localhost%3A&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You&#39;re most likely developing this on a local development machine, a.k.a. your localhost, which is impossible for Mux to notify directly.&lt;/p&gt;
&lt;p&gt;The easiest way to make your localhost available to Mux and the outside world is by providing a secure tunnel using a tool like ngrok. It is a free tool and works great for our purpose.&lt;/p&gt;
&lt;p&gt;So, if your Phoenix app runs on localhost:4000, all you need to do is run ngrok with that port number, like so:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;ngrok http &lt;span class=&quot;token number&quot;&gt;4000&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://marcelfahle.net/img/videoupload-screen3-ngrok.png&quot; alt=&quot;ngrok&quot;&gt;&lt;/p&gt;
&lt;p&gt;You&#39;ll get back a URL, which we can then add to our Mux dashboard under Settings -&amp;gt; Webhooks -&amp;gt; Create new Webhook.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marcelfahle.net/img/videoupload-screen4-webhook.png&quot; alt=&quot;Create a Webhook in Mux&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once we&#39;ve created a webhook, we still need to grab the signing secret to verify that all incoming webhooks are actually from Mux and are legit. So, on the webhooks page, click on the &amp;quot;Show Signing Secret&amp;quot; button of your newly created webhook and add that secret to your Mux config:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marcelfahle.net/img/videoupload-screen5-webhooksecret.png&quot; alt=&quot;Show Webhook Signing Secret in Mux&quot;&gt;&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# config/dev.exs&lt;/span&gt;&lt;br&gt;&lt;br&gt;config &lt;span class=&quot;token atom symbol&quot;&gt;:mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;access_token_id:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MUX_TOKEN_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;access_token_secret:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MUX_TOKEN_SECRET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;webhook_secret:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;WEBHOOK_SIGNING_SECRET&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we&#39;ll set up a controller to process those webhooks. We&#39;ll start with a very simple version that logs every incoming webhook to our terminal and responds with a success message:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/webhook_controller.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;WatermarkrWeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;WebhookController&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;WatermarkrWeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:controller&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;message:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webhook received&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we now upload a video, we should see the notifications from Mux pouring into your server console:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;%&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;accessor&quot;&lt;/span&gt; =&gt; nil&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;accessor_source&quot;&lt;/span&gt; =&gt; nil&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;attempts&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;created_at&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2023-01-21T02:58:10.905000Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;data&quot;&lt;/span&gt; =&gt; %&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;aspect_ratio&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;16:9&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;created_at&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token number&quot;&gt;1674269889&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;duration&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token number&quot;&gt;37.1811&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;7zFS02acd3buTmutRrC78yvgPVgcLF4hG&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;master_access&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;max_stored_frame_rate&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token number&quot;&gt;29.97&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;max_stored_resolution&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;mp4_support&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;passthrough&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8dd4676b-579d-4bc7-8896-3fe6638decab&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;playback_ids&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;      %&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nz6AXjfZUjOT0scdnbGB1IuqHkzj9CQtQ&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;policy&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ready&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;tracks&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;      %&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;duration&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token number&quot;&gt;37.137104&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;WFUxs018nVTeog6fczNEWofPBp4N00asB&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;max_channel_layout&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;stereo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;max_channels&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;type&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;audio&quot;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      %&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;duration&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token number&quot;&gt;37.1371&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yrwviIQ4tcadfRmIcNa3fLGhC00I01JmQxMdV9rs9hd100&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;max_frame_rate&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token number&quot;&gt;29.97&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;max_height&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token number&quot;&gt;1080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;max_width&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token number&quot;&gt;1920&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;type&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;video&quot;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;upload_id&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UvkzTWnemeMESr0002Z6mkvibNPuVtlfu4g&quot;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;environment&quot;&lt;/span&gt; =&gt; %&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4p44dv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Development&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;9133be58-0797-4d0c-8123-2a302bd9ee9d&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;object&quot;&lt;/span&gt; =&gt; %&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;7zFS0edgY3buTmutRrC78yvgPVgcLF4hG&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;type&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;asset&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;request_id&quot;&lt;/span&gt; =&gt; nil&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;type&quot;&lt;/span&gt; =&gt; &lt;span class=&quot;token string&quot;&gt;&quot;video.asset.ready&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&#39;s a lot of information in those payloads, but the most interesting parts for us right now are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;type&lt;/code&gt;: There are many types of webhook events (complete list of webhook events), but we&#39;re currently only interested in &amp;quot;video.asset.ready&amp;quot;, which tells us that the video has been encoded successfully and is ready for playback.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data.id&lt;/code&gt;: The ID of the Mux asset, which we can use to interact and request information about the video we&#39;ve just uploaded&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data.playback_id[].id&lt;/code&gt;: The Playback ID, which we can use to actually play our video&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data.passthrough&lt;/code&gt;: our own video ID, which we added to the upload earlier.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, let&#39;s now parse the webhook payloads, grab the above information, and update our video in our database.&lt;/p&gt;
&lt;p&gt;It&#39;s always a good idea to verify incoming webhook requests to make sure it&#39;s actually Mux sending us that data. But to do any kind of verification, we need the raw payload that Mux is sending us, which we don&#39;t have anymore at this point. The data already went through Phoenix&#39;s Plug pipeline and has been parsed into a map, which is usually much more useful. But in our case, we need the raw JSON data, and the trick is to stick a copy of it onto our connection before it gets turned into a Map. We&#39;ll write a little helper script to do just that (I got that solution from &lt;a href=&quot;https://github.com/phoenixframework/phoenix/issues/459#issuecomment-440820663&quot;&gt;GitHub&lt;/a&gt; and &lt;a href=&quot;https://stackoverflow.com/a/51587646/1136313&quot;&gt;Stack Overflow&lt;/a&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr/body_reader.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Watermarkr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;BodyReader&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;read_body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; conn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Plug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Conn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read_body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    conn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update_in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:raw_body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;body &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token argument variable&quot;&gt;&amp;amp;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; conn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can provide this function to the &lt;code&gt;:body_reader&amp;quot;&lt;/code&gt; option of the Plug.Parsers behavior:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/endpoint.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;  plug &lt;span class=&quot;token module class-name&quot;&gt;Plug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Parsers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;parsers:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:urlencoded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:multipart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;pass:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;*/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;body_reader:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Watermarkr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;BodyReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:read_body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;json_decoder:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Phoenix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json_library&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Mux SDK comes with a &lt;a href=&quot;https://hexdocs.pm/mux/Mux.Webhooks.html#verify_header/4&quot;&gt;Mux.Webhooks.verify_header/4&lt;/a&gt; function, which makes the last verification step a breeze. We&#39;ll give it our webhook signing secret, which we set up earlier, as well as the webhook&#39;s signature header and the raw JSON that we now have access to:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/webhook_controller.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;WatermarkrWeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;WebhookController&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;WatermarkrWeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:controller&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Watermarkr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Media&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# grab the mux signature and full payload for verification&lt;/span&gt;&lt;br&gt;    signature_header &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_req_header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mux-signature&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    raw_body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;raw_body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# verify that the incoming webhook is legit and only then&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# process is. If verfication fails, return an error.&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Webhooks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;verify_header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;         raw_body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;         signature_header&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token module class-name&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch_env!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:webhook_secret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;       &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;# process the webhook payload&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token function&quot;&gt;process_mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;# respond to the webhook that it has been processed&lt;/span&gt;&lt;br&gt;        conn&lt;br&gt;        &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;put_resp_content_type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;send_resp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;br&gt;        conn&lt;br&gt;        &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;put_status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;message:&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# we pattern match on the webhook type, that way&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# it&#39;s easy to add more types to process&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;process_mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;video.asset.ready&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; asset_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;passthrough&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; video_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;playback_ids&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; playback_ids&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# find our video in the database using the&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# passthrough value and updated it with the&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# asset and playback ids.&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# A Mux asset can have multiple playback ids but&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# in our example we always grab the first one.&lt;/span&gt;&lt;br&gt;    video &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_video!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    playback_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;playback_ids&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update_video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;asset_id:&lt;/span&gt; asset_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;playback_id:&lt;/span&gt; playback_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# ignore all other webhook events&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;process_mux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _asset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;bonus%3A-live-updates&quot;&gt;Bonus: Live updates &lt;a class=&quot;direct-link&quot; href=&quot;#bonus%3A-live-updates&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cool, we&#39;re now updating our database after receiving webhooks from Mux. But waiting for those webhooks and manually refreshing our browser to see the result is not very LiveView&#39;y. Let&#39;s update our LiveView once our video has been updated.&lt;/p&gt;
&lt;p&gt;We&#39;ll use &lt;a href=&quot;https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html&quot;&gt;Phoenix.PubSub&lt;/a&gt; to notify our LiveView about the updates. Everything is already set up on Phoenix&#39;s end, so all we need to do is to broadcast our message. I tend to call my broadcast functions from within context modules, especially regarding any sort of CRUD action, so let&#39;s do this here as well. We&#39;ll write a private &lt;code&gt;notify/2&lt;/code&gt; function that we can then stick into our pipelines:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr/media.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update_video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attrs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    video&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;attrs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:video_updated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;Phoenix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;broadcast&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Watermarkr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;video_updates&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    video&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, back in our Index LiveView, we first need to subscribe to the topic of our broadcasts (&lt;code&gt;video_updates&lt;/code&gt; in our example):&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/live/video_live/index.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;connected?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token module class-name&quot;&gt;Phoenix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Watermarkr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;video_updates&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then, further down, we&#39;ll add a &lt;a href=&quot;https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#c:handle_info/2&quot;&gt;handle_info/2&lt;/a&gt; callback and refetch all our videos if there has been an update:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/live/video_live/index.ex&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attribute variable&quot;&gt;@impl&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:video_updated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _video_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:videos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;list_videos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; list_videos &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;list_videos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&#39;s, of course, a lot of room for optimization here since we don&#39;t need to fetch the whole list of videos every time there&#39;s a little update, but I&#39;ll leave that exercise to you, dear reader. :)&lt;/p&gt;
&lt;h2 id=&quot;video-playback-and-thumbnails&quot;&gt;Video Playback and Thumbnails &lt;a class=&quot;direct-link&quot; href=&quot;#video-playback-and-thumbnails&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that we have all the info from Mux snug in our database, we can use that info to show some thumbnails in our index view and then even (drumroll) play the video.&lt;/p&gt;
&lt;p&gt;Let&#39;s head to our index template and update the videos table to show a thumbnail if we have a video or a placeholder if nothing&#39;s there yet. We&#39;ll also remove the asset and playback ids from the table that have been added by our generators earlier:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/live/video_live/index.html.heex&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;table id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;videos&quot;&lt;/span&gt; rows&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attribute variable&quot;&gt;@videos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; row_click&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;JS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;~p&lt;span class=&quot;token string&quot;&gt;&quot;/videos/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token argument variable&quot;&gt;&amp;amp;1&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:col&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:let&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Thumbnail&quot;&lt;/span&gt;&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;img &lt;span class=&quot;token atom symbol&quot;&gt;:if&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;playback_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; src&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://image.mux.com/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;playback_id&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/thumbnail.webp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;120&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;img &lt;span class=&quot;token atom symbol&quot;&gt;:if&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;playback_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; src&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://via.placeholder.com/120x68&quot;&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;120&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:col&lt;/span&gt;&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:col&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:let&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; label&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Title&quot;&lt;/span&gt;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:col&lt;/span&gt;&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:action&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:let&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://marcelfahle.net/img/videoupload-screen6-liveupdates.png&quot; alt=&quot;Our updated video list&quot;&gt;&lt;/p&gt;
&lt;p&gt;For the final (and arguably most fun) piece of the puzzle, let us bring in the new &lt;a href=&quot;https://www.mux.com/player&quot;&gt;Mux Player&lt;/a&gt;. The easiest way to do that is to load it from a CDN inside our root layout:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;# lib/watermarkr_web/components/layouts/root.html.heex&lt;br&gt;[...]&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bg-white antialiased&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &amp;lt;%= @inner_content %&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;https://cdn.jsdelivr.net/npm/@mux/mux-player&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, all that&#39;s left is adding it to our show template:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# lib/watermarkr_web/live/video_live/show.html.heex&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:actions&lt;/span&gt;&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;header&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;mux&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;player&lt;br&gt;  &lt;span class=&quot;token atom symbol&quot;&gt;:if&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attribute variable&quot;&gt;@video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;playback_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  stream&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;on-demand&quot;&lt;/span&gt;&lt;br&gt;  playback&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attribute variable&quot;&gt;@video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;playback_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  metadata&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;video&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;title&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attribute variable&quot;&gt;@video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mux&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;player&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there you have it: a working upload for large video files, a mighty fine encoding engine and API powered by Mux, and modern video playback that works on all platforms. Boom!&lt;br&gt;
In the next post, We&#39;ll explore how watermarking with Mux works, as I&#39;m curious myself. &lt;strong&gt;Always be learning!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Were the technical explanations around Uploads in this post clear? Did everything work out as expected?&lt;br&gt;
Is there something you didn&#39;t like?&lt;br&gt;
Please, let me know and send all feedback my way: &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;@marcelfahle&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Reboot</title>
		<link href="https://marcelfahle.net/posts/2023-10-25-reboot/"/>
		<updated>2023-10-25T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/2023-10-25-reboot/</id>
		<content type="html">&lt;p&gt;Back in 2003, I dove into the world of blogging. I wrote about tech, coding, and random thoughts that popped into my head. Back then, it felt easy, like riding a bike downhill. I wasn&#39;t concerned with what others thought. I just shared and had fun.&lt;/p&gt;
&lt;p&gt;But as time passed, things changed. I met many smart people online and in person. My craft improved, and with each new thing I learned, my understanding of what was considered &amp;quot;good&amp;quot; shifted. I saw brilliance around me and set higher standards for myself. Slowly, without even realizing it, I found myself sharing less and less. What started as an occasional hesitation turned into a habit of holding back.&lt;/p&gt;
&lt;p&gt;Then, something clicked. A few weeks ago, I was at &lt;a href=&quot;https://microconf.com/europe&quot;&gt;Microconf Europe&lt;/a&gt;. Even though I knew less about business and indie hacking than I do about programming, I engaged in meaningful conversations. I listened, shared my thoughts, and felt actually confident. It struck me that my confidence wasn&#39;t about how much I knew, but about being genuine and excited to learn.&lt;/p&gt;
&lt;p&gt;It made me think: Maybe writing programming tutorials isn’t what I truly wanted after all. It wasn&#39;t about showcasing perfection but about embracing exploration. As we evolve, our passions might shift, and that&#39;s perfectly okay.&lt;/p&gt;
&lt;p&gt;Moving forward I want to talk more about business, building things, and growing as a person. It&#39;s not about knowing it all, but about being curious and learning. Often whatever I&#39;ll write will be just for myself, to help me and clarify my thinking. A blog is supposed to be a journal after all. I hope you&#39;ll join me on this new path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ever felt a push to try something new? What did you learn from it? Hit me up on Twitter and tell me all about it: &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;@marcelfahle&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Why we run</title>
		<link href="https://marcelfahle.net/posts/2023-11-19-why-we-run/"/>
		<updated>2023-11-19T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/2023-11-19-why-we-run/</id>
		<content type="html">
&lt;p&gt;My wife Monika ran the &lt;a href=&quot;https://costablancatrails.com/&quot; target=&quot;_blank&quot;&gt;Costa Blanca Trails&lt;/a&gt; yesterday, a 65km mountain race with 3500m elevation gain. She did a few races of that length in recent times, but this one was different.&lt;/p&gt;

&lt;p&gt;About 15k from the finish, she hit a wall. Hard. She called me, her voice cracking with pain and exhaustion. &quot;I can&#39;t do this anymore, I&#39;m done.&quot; she said, for the first time ever. I could hear the raw emotion, on the brink of giving up. But I knew she had more in her. In those moments, through a few texts back and forth, I saw her find her grit again.&lt;/p&gt;




&lt;p&gt;Fast forward to the finish line. There she was, crossing it, in a mix of exhaustion and triumph. It&#39;s strange how these races work. One minute you&#39;re questioning why you&#39;re even doing it, and the next, you&#39;re filled with this intense feeling of achievement.&lt;/p&gt;

&lt;p&gt;Watching her, I thought about how these races can teach us extraordinary things. It&#39;s in these moments, when you&#39;re pushed to your limits, that you really feel alive. One minute you&#39;re in the thick of it, and the next, you&#39;re already thinking about the next challenge.&lt;/p&gt;


&lt;p&gt;Here&#39;s what I&#39;ve learned: embrace the hard things. They bring out feelings that can&#39;t be replicated anywhere else. In those moments of pure unadulterated achievement, you find a part of yourself that would otherwise remain hidden in comfort.&lt;/p&gt;

&lt;p&gt;This isn&#39;t about running or endurance. It&#39;s about learning to push through, to find that extra bit of strength when you think you&#39;re done. And that&#39;s a lesson that&#39;s valuable in every part of life, not just on the trail.&lt;/p&gt;

&lt;p&gt;Monika&#39;s race wasn&#39;t just a physical challenge; it was a journey of self-discovery. And that&#39;s probably the biggest win of all.&lt;/p&gt;

&lt;p&gt;Here&#39;s the story in pictures:&lt;/p&gt;


&lt;div class=&quot;img-wrapper&quot;&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2023-whywerun-text1.jpg&quot; alt=&quot;Breaking point&quot;&gt;
        &lt;figcaption&gt;Breaking Point&lt;/figcaption&gt;
    &lt;/figure&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2023-whywerun-text2.jpg&quot; alt=&quot;Not far anymore&quot;&gt;
        &lt;figcaption&gt;Not far anymore&lt;/figcaption&gt;
    &lt;/figure&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2023-whywerun-text3.jpg&quot; alt=&quot;Bring it home. Goggins that shit! Husband is pumped.&quot;&gt;
        &lt;figcaption&gt;Bring it home. Goggins that shit! Husband is pumped.&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;div class=&quot;img-wrapper&quot;&gt;
  &lt;figure&gt;
      &lt;img src=&quot;https://marcelfahle.net/img/blog/2023-whywerun-finish.jpg&quot; alt=&quot;Done!&quot;&gt;
      &lt;figcaption&gt;Done. Time for Team Celebration.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;div class=&quot;img-wrapper&quot;&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2023-whywerun-on-puig-campana.jpg&quot; alt=&quot;View from Puig Campana after the first climb. Benidorm below.&quot;&gt;
        &lt;figcaption&gt;View from Puig Campana after the first climb. Benidorm below.&lt;/figcaption&gt;
    &lt;/figure&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2023-whywerun-puig-campana-from-aitana.jpg&quot; alt=&quot;View from Aitana, the highest mountain in the Alicante province. Puig Campana from before in the distance.&quot;&gt;
        &lt;figcaption&gt;View from Aitana, the highest mountain in the Alicante province. Puig Campana from before in the distance.&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What about you? When was the last time you pushed out of your comfort zone, and what did it teach you about yourself? Tweet at me at &lt;a href=&quot;https://twitter.com/marcelfahle&quot; target=&quot;_blank&quot;&gt;@marcelfahle&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>4AM Club</title>
		<link href="https://marcelfahle.net/posts/2023-12-21-4am-club/"/>
		<updated>2023-12-21T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/2023-12-21-4am-club/</id>
		<content type="html">&lt;p&gt;I wake up every day before 04:00. That&#39;s seven days a week. Every time the subject comes up I get looks that range between fascination, respect but also plenty of &amp;quot;what the fuck dude?&amp;quot;.&lt;/p&gt;
&lt;p&gt;Over the years, I came across many posts about the &amp;quot;4AM club.&amp;quot; Usually, those are about incredible business success (that&#39;s how you get it. Pay attention, folks!) tied to the daily routines of unrelatable people like Tim Cook or Michelle Obama.&lt;/p&gt;
&lt;p&gt;If it only were that easy.&lt;/p&gt;
&lt;p&gt;I don&#39;t see myself as successful in the conventional sense. Yet, I&#39;m very happy with where I am in life, and it&#39;s not because I wake up early.&lt;/p&gt;
&lt;p&gt;I guess the biggest indicator of this habit leading anywhere near financial success is probably discipline. If you can get up daily before everyone else, you&#39;re likely pretty disciplined.&lt;/p&gt;
&lt;p&gt;But then again, &lt;a href=&quot;https://jamesclear.com/new-habit&quot;&gt;you might&#39;ve just disciplined yourself for 21 days&lt;/a&gt; and established a habit. Because for me, it&#39;s become automatic. It&#39;s not particularly difficult or challenging anymore. In fact, I&#39;m excited when I hear my alarm and can jump out of bed.&lt;/p&gt;
&lt;p&gt;That said, why I decided to start my days early has practical reasons. I have a bunch of side projects that I want to work on and can do so only outside my day job hours. Usually that would mean late afternoons, evenings or late nights. But, I have two little kids and I reserve afternoons and evenings for them.&lt;/p&gt;
&lt;p&gt;Add to that, after working a full day and playing with the little monkeys all afternoon, I&#39;m usually pretty spent in the evenings. Picking out a Netflix movie is already a major challenge at this point in the day. Well, it actually always is but you get what I&#39;m saying.&lt;/p&gt;
&lt;p&gt;That&#39;s why I&#39;ve decided to go to sleep with them. Read stories together, sleep, recharge. Lights out at 8:30pm.&lt;/p&gt;
&lt;p&gt;To some friends here in Spain, that sounds odd and draws more skepticism than my early rising. When we go to sleep it&#39;s most people&#39;s dinner time over here. But, to me that whole thing &lt;a href=&quot;https://www.washingtonpost.com/news/worldviews/wp/2013/09/26/spaniards-are-less-productive-constantly-tired-because-spain-is-in-the-wrong-time-zone/&quot;&gt;is yet another Nazi related mistake.&lt;/a&gt; It&#39;s also the reason why our family keeps all our social gatherings and whatnot during lunchtime.&lt;/p&gt;
&lt;p&gt;Anyway, I then came to realize that my morning hours are even more precious than I thought. Take away the side projects and replace it with general me-time. It has been studied widely that when you lead a busy life, as one does with two toddlers around, it is pretty darn healthy to have time to do whatever the hell you like.&lt;/p&gt;
&lt;p&gt;To me right now, it&#39;s creating things. My side hustles. Those bring me joy, excitement and self-fulfilment. That might change at some point and could use that time for running and reading, but it&#39;s still my time. And I think I&#39;m much better off protecting that.&lt;/p&gt;
&lt;p&gt;The exact time doesn&#39;t matter. Waking up at 3:30 am works for me, but it might not for others. However, being intentional about these things is a better approach to life than thinking waking up early will make you successful. That&#39;s not Tim Cook&#39;s secret sauce either. It does feel cool though &lt;a href=&quot;https://twitter.com/jockowillink/status/1737437242820591962&quot;&gt;being up before Jocko does&lt;/a&gt;. 🙂&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How about you? Are you a morning person? Are you intentional about your time? Hit me up &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;on the twitters&lt;/a&gt;, I would love to hear more about that!&lt;/strong&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Writing, Not Hustling</title>
		<link href="https://marcelfahle.net/posts/writing-not-hustling/"/>
		<updated>2024-01-31T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/writing-not-hustling/</id>
		<content type="html">&lt;p&gt;I learned that I work best if I write down what I think. Joan Didion nailed it when she said, “I don&#39;t know what I think until I write it down.” This blog is where I want to do more of that – think out loud, minus the fuss about making it perfect, polishing or selling something.&lt;/p&gt;
&lt;p&gt;So I&#39;m shifting gears, &lt;a href=&quot;https://marcelfahle.net/posts/2023-10-25-reboot&quot;&gt;yet again&lt;/a&gt;. This site isn’t for personal branding or hustling for likes and reach. It’s for me, and maybe for you, if you’re into exploring thoughts without the noise.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://joelhooks.com/on-writing-more/&quot;&gt;Joel Hooks&lt;/a&gt; actually put this once in a way that I&#39;m super into:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The idea of a &amp;quot;blog&amp;quot; needs to get over itself. Everybody is treating writing as a &amp;quot;content marketing strategy&amp;quot; and using it to &amp;quot;build a personal brand&amp;quot; which leads to the fundamental flawed idea that everything you post has to be polished to perfection and ready to be consumed.&lt;/p&gt;
&lt;p&gt;Bullshit.&lt;/p&gt;
&lt;p&gt;This idea is toxic and led me to publish less and less over time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s the vibe I want here. More thinking, less showcasing and hustle.&lt;/p&gt;
&lt;p&gt;This place is for us to think, share, and maybe learn a bit along the way. No pressure, no polish. Just raw thoughts, figuring stuff out together.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Running in 2024</title>
		<link href="https://marcelfahle.net/posts/running-2024/"/>
		<updated>2024-02-10T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/running-2024/</id>
		<content type="html">
&lt;p&gt;In 2019, I was training for my first 100k mountain race, the &lt;a href=&quot;https://ultrapirineu.com/en/&quot; target=&quot;_blank&quot;&gt;Ultra Pirineu&lt;/a&gt;, an absolute dream race for me. It was a good year. Besides a bunch of short races, Monika and I did &lt;a href=&quot;https://www.strava.com/activities/2057244342&quot; target=&quot;_blank&quot;&gt;back&lt;/a&gt; to &lt;a href=&quot;https://www.strava.com/activities/2059827037&quot; target=&quot;_blank&quot;&gt;back&lt;/a&gt; 40Ks to set the tone of the year in January. In March I took a wrong turn at the &lt;a href=&quot;https://www.strava.com/activities/2201960687&quot; target=&quot;_blank&quot;&gt;Perimetral in Benissa&lt;/a&gt; and then had &lt;a href=&quot;https://www.strava.com/activities/2415636971&quot; target=&quot;_blank&quot;&gt;my first DNF in Ribes at Els Bastions&lt;/a&gt;. I also finished the &lt;a href=&quot;https://www.strava.com/activities/2607599478/overview&quot; target=&quot;_blank&quot;&gt;Trail Naut Aran&lt;/a&gt;, the predecessor (?) of &lt;a href=&quot;https://valdaran.utmb.world/&quot; target=&quot;_blank&quot;&gt;Val d&#39;Aran by UTMB&lt;/a&gt; around the stunning Aran Valley.&lt;/p&gt;

&lt;p&gt;But then, a month before the big race, life took an exciting turn when we had our first kid. Ever the optimist, I thought I can become a father and run my race. What I didn&#39;t expect though was how little I would care anymore about my running hobby when our daughter entered our lives. Nothing else mattered anymore.&lt;/p&gt;

&lt;div class=&quot;img-wrapper&quot;&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2024-running-strava.jpg&quot; alt=&quot;My training before we had our first child&quot;&gt;
        &lt;figcaption&gt;My training before we had our first child&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;


&lt;p&gt;And so it went. I would only run occasionally, to keep the balance, but my philosophy was to not take time away from the kid to pursue my hobby. I was 41 years old at that point, I had plenty of time to do my shit in all those years. All in on family from now on.&lt;/p&gt;

&lt;p&gt;Then the pandemic hit, and shortly after we had another kid (yay!). Having two little monkeys felt like the intensity was cranked up to 10x but it was, and still is, the best time of my life.&lt;/p&gt;

&lt;p&gt;But I still wanted to run. The only races I did until now was &lt;a href=&quot;https://www.strava.com/activities/6256424707&quot; target=&quot;_blank&quot;&gt;Behobia/SS in 2021&lt;/a&gt; and the inaugural &lt;a href=&quot;https://www.strava.com/activities/8657278131&quot; target=&quot;_blank&quot;&gt;Atzavares Trail&lt;/a&gt; in 2023. But neither was a big challenge of the kind I used to do.&lt;/p&gt;

&lt;p&gt;Now, that the kids are a bit older, 4 and 2, I think we can dial it up a notch again. In mid-January I started training again a bit more than regularly.&lt;/p&gt;

&lt;p&gt;I don&#39;t have any real races planned but there&#39;s one fun challenge that I would like to tackle this year: Where my in-laws live, in Lithuania, near the Latvian border, there is this route along those endless wheat, barley and potato fields that&#39;s roughly 42-45KM long. Flat, asphalt, empty, silent. I always thought this was a fantastic route for a Marathon.&lt;/p&gt;

&lt;div class=&quot;img-wrapper&quot;&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2024-running-project2024.jpg&quot; alt=&quot;My running project for 2024&quot;&gt;
        &lt;figcaption&gt;My running project for 2024&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;This year I&#39;m going to run it. In July or August, not sure yet. It&#39;s more or less a normal marathon but fully self supported, nobody to cheer you on, one or two shops on the route, and just a whole bunch of nothing. That&#39;s the kind of challenge I&#39;m interested in this year.&lt;/p&gt;

&lt;p&gt;I will probably post a few of my workouts on the Twitters, for accountability reasons (and to inspire &lt;a href=&quot;https://twitter.com/saasmakermac&quot; target=&quot;_blank&quot;&gt;Mac&lt;/a&gt;, with whom I want to run the &lt;a href=&quot;https://www.mychip.es/e/cami-bandolers-senija-ctm-2324&quot; target=&quot;_blank&quot;&gt;Camí dels Bandolers&lt;/a&gt; in April 😁)&lt;/p&gt;

&lt;p&gt;Happy to fire up the engines again and see where it takes me. Let&#39;s fucking go!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you have any challenges set for this year? Hit me up on &lt;a href=&quot;https://twitter.com/marcelfahle&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt;, I want to know all about it!&lt;/strong&gt;&lt;/p&gt;

</content>
	</entry>
	
	<entry>
		<title>Writing as a habit</title>
		<link href="https://marcelfahle.net/posts/2024-05-07-writing-habit/"/>
		<updated>2024-05-07T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/2024-05-07-writing-habit/</id>
		<content type="html">&lt;p&gt;I recently signed up for Dickie Bush &amp;amp; Nicolas Cole&#39;s cohort-based course, &lt;a href=&quot;https://www.ship30for30.com/&quot;&gt;Ship 30 for 30&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here are a few reasons why:&lt;/p&gt;
&lt;h3 id=&quot;writing-improves-my-thinking.&quot;&gt;Writing improves my thinking. &lt;a class=&quot;direct-link&quot; href=&quot;#writing-improves-my-thinking.&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;By writing my thoughts down and putting the loose ramblings of the mind into actual words on a page, I have to clarify and explain. By doing that, I can quickly realize if loose thoughts are actually valid ideas worth exploring.&lt;/p&gt;
&lt;h3 id=&quot;it-flexes-my-publishing-muscle.&quot;&gt;It flexes my publishing muscle. &lt;a class=&quot;direct-link&quot; href=&quot;#it-flexes-my-publishing-muscle.&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&#39;m a software engineer and terrible at marketing myself. I also believe that I&#39;m huge imposter.&lt;/p&gt;
&lt;p&gt;Instead of putting something I&#39;ve built out there, I usually come up with more and more things to add or improve, or some other distraction altogether. Publishing terrifies me for some reason.&lt;/p&gt;
&lt;p&gt;A piece of writing is a very manageable unit of work, less impactful than when someone uses and hates my software. It&#39;s much easier to hit publish and get feedback. And doing that repeatedly will help me become better at putting myself out there and being vulnerable.&lt;/p&gt;
&lt;h3 id=&quot;it-makes-me-a-better-communicator.&quot;&gt;It makes me a better communicator. &lt;a class=&quot;direct-link&quot; href=&quot;#it-makes-me-a-better-communicator.&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Being a good communicator is an essential skill, not only in professional environments. Efficient communication can make or break all kinds of engagements and relationships. I believe that the constraint of putting my thoughts into writing will make me a better communicator.&lt;/p&gt;
&lt;h3 id=&quot;i-practice-%22noticing%22.&quot;&gt;I practice &amp;quot;noticing&amp;quot;. &lt;a class=&quot;direct-link&quot; href=&quot;#i-practice-%22noticing%22.&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;That&#39;s a skill that I admire most notably on great comedians. They&#39;re able to turn the most mundane situations into engaging and hilarious stories. They notice the little things in life and realize that&#39;s something they can connect with the audience on.&lt;/p&gt;
&lt;p&gt;So, as I walk through life, constantly alert for things to write an atomic essay or two about, I believe that I&#39;ll also flex that &#39;noticing&#39; muscle&lt;/p&gt;
&lt;p&gt;So, I hope you can join me. I&#39;m excited to see where all of this leads.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Run Slow to Run Fast</title>
		<link href="https://marcelfahle.net/posts/runslow/"/>
		<updated>2024-05-08T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/runslow/</id>
		<content type="html">&lt;p&gt;Most people I talk to are approaching becoming a runner the wrong way.&lt;/p&gt;
&lt;p&gt;They treat it as a grueling workout, something that has to be difficult. You have to sweat and pant, your head needs to be tomato-red - after all, it&#39;s a sport!&lt;/p&gt;
&lt;p&gt;And while there are great benefits to high-intensity workouts, if you want to become a runner, there&#39;s a better approach. You need to slow down.&lt;/p&gt;
&lt;p&gt;Here&#39;s why:&lt;/p&gt;
&lt;h2 id=&quot;slow-running-is-a-shortcut-to-success&quot;&gt;Slow running is a shortcut to success &lt;a class=&quot;direct-link&quot; href=&quot;#slow-running-is-a-shortcut-to-success&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The aerobic system is your body&#39;s way of using oxygen to produce energy. It&#39;s what makes you able to sustain longer runs and workouts.&lt;/p&gt;
&lt;p&gt;Running too fast, leading to rapid and heavy breathing, is like over-revving a car engine. It doesn&#39;t give the aerobic system a chance to work efficiently and build endurance in the first place. It also leads to muscle fatigue and decreased performance.&lt;/p&gt;
&lt;p&gt;By running slow, you leave your body with enough oxygen to provide you with energy and build a strong foundation for aerobic endurance, which ultimately lets you run faster.&lt;/p&gt;
&lt;h2 id=&quot;you-reduce-the-risk-of-injury&quot;&gt;You reduce the risk of injury &lt;a class=&quot;direct-link&quot; href=&quot;#you-reduce-the-risk-of-injury&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Running is a high-impact activity and puts a lot of stress on your muscles, joints and bones. Starting slow allows all these body parts to adapt gradually and safely.&lt;/p&gt;
&lt;h2 id=&quot;encourages-consistency&quot;&gt;Encourages consistency &lt;a class=&quot;direct-link&quot; href=&quot;#encourages-consistency&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For beginners, running at a slower pace can make the exercise feel more manageable and enjoyable, which is essential for forming a new habit.&lt;/p&gt;
&lt;p&gt;I&#39;ve had conversations about this countless of times, and I know, it&#39;s embarrassing to move this slow, when you could technically &amp;quot;run&amp;quot; faster. Trust me, I&#39;ve been there. When I started out, I always ran in the dark, so people won&#39;t see me.&lt;/p&gt;
&lt;p&gt;Whatever you have to do, to get over this feeling, trust me, it&#39;s worth it.&lt;/p&gt;
&lt;p&gt;Hit me up on &lt;a href=&quot;https://www.strava.com/athletes/18230818&quot;&gt;Strava&lt;/a&gt;, and we&#39;ll go through this together. 💪&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>The Perfect Lead Magnet</title>
		<link href="https://marcelfahle.net/posts/the-perfect-lead-magnet/"/>
		<updated>2024-05-09T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/the-perfect-lead-magnet/</id>
		<content type="html">&lt;p&gt;&lt;a href=&quot;https://mds.is/&quot;&gt;Matt D. Smith&lt;/a&gt;, or, &lt;a href=&quot;https://twitter.com/mds&quot;&gt;MDS&lt;/a&gt;, as he&#39;s mostly known on the internet, is a designer, who runs the cohort based UI design course &lt;a href=&quot;https://shiftnudge.com/&quot;&gt;Shift Nudge&lt;/a&gt; (enrolment opens by the way June 3rd).&lt;/p&gt;
&lt;p&gt;I&#39;ve never taken the course myself but if designing would be part of my income streams, that&#39;s the first resource I&#39;d turn to. In fact, I hear it&#39;s so good, that even as a developer, I have to battle my inner FOMO demons every time MDS opens the doors.&lt;/p&gt;
&lt;p&gt;But anyway, enough about Shift Nudge. I want to quickly highlight a few things I&#39;ve noticed about the new course Matt just published two weeks ago: &lt;a href=&quot;https://shiftnudge.com/figma&quot;&gt;Figma 101&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think that course is one of the most perfectly executed lead magnets I&#39;ve seen so far, and here&#39;s why:&lt;/p&gt;
&lt;h3 id=&quot;the-level-of-quality-matches-the-flagship-product&quot;&gt;The level of quality matches the flagship product &lt;a class=&quot;direct-link&quot; href=&quot;#the-level-of-quality-matches-the-flagship-product&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is not a half-assed AI-generated ebook for the sake of getting emails, this is a proper course. The real deal. A course that easily matches, if not outperforms, many of its paid competitors.&lt;/p&gt;
&lt;p&gt;The visuals, the editing, the pacing, the teaching style, it&#39;s all there. VALUE, but in capital letters. The best part:&lt;/p&gt;
&lt;h3 id=&quot;it&#39;s-a-free-course&quot;&gt;It&#39;s a free course &lt;a class=&quot;direct-link&quot; href=&quot;#it&#39;s-a-free-course&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Yup, it&#39;s free. And I personally love this. Not because I&#39;m cheap, but because I believe it maximizes Matt&#39;s reach. People will share it (as I did). People talk about and link to it (as I did - just now 😁), strengthen his brand even more.&lt;/p&gt;
&lt;p&gt;He could&#39;ve easily made a smooth 5-figures on that course, but how short sighted would that&#39;ve been? Instead he did something else, and that brings me to my favorite part:&lt;/p&gt;
&lt;h3 id=&quot;prepares-newcomers-for-the-main-dish&quot;&gt;Prepares newcomers for the main dish &lt;a class=&quot;direct-link&quot; href=&quot;#prepares-newcomers-for-the-main-dish&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Figma 101 basically teaches new users the skills that&#39;ll help them follow and succeed with MDS&#39;s premium offering, which is Shift Nudge. That is absolutely genius, you not only water their mouths with such a course, but also fast-track their readiness to succeed with the main thing.&lt;/p&gt;
&lt;p&gt;Figma 101 will undoubtedly get more people to engage than Shift Nudge alone and with it, Matt not only showcases his expertise and the high quality of his courses, but he also builds trust and goodwill among his people.&lt;/p&gt;
&lt;p&gt;To me, MDS giving something so substantial for free is a breath of fresh air. It&#39;s a powerful reminder that in a world eager to make a quick buck, the long game of genuine value creation leads to more meaningful and sustainable success.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Stay Sharp: Resist Overusing AI</title>
		<link href="https://marcelfahle.net/posts/staysharp/"/>
		<updated>2024-05-10T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/staysharp/</id>
		<content type="html">&lt;p&gt;I love all the advancements in Artificial Intelligence, and I use tools like ChatGPT, GitHub Copilot, and a bunch of others many times per day.&lt;/p&gt;
&lt;p&gt;However, I&#39;ve experienced the consequences of becoming too dependent on these shiny new toys. Sure, we can be so much more productive now, crank out work that seems good in a fraction of the time that it used to take. I&#39;m doing this daily.&lt;/p&gt;
&lt;p&gt;I urge you, though, to not overdo it, especially in fields of work you care about and want to grow. Here&#39;s why:&lt;/p&gt;
&lt;h3 id=&quot;ai-is-a-shortcut&quot;&gt;AI is a shortcut &lt;a class=&quot;direct-link&quot; href=&quot;#ai-is-a-shortcut&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Shortcuts are never good for anything when it comes to learning. Taking the easy road doesn&#39;t lead to deep understanding. Your foundations of knowledge will crumble if you rely too heavily on AI.&lt;/p&gt;
&lt;h3 id=&quot;diminished-problem-solving-skills&quot;&gt;Diminished Problem-Solving Skills &lt;a class=&quot;direct-link&quot; href=&quot;#diminished-problem-solving-skills&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We are so blessed that we have our brains to create things and solve complex problems. Those things can&#39;t be replicated by AI. Regularly turning to AI can erode your critical thinking and problem-solving skills.&lt;/p&gt;
&lt;h3 id=&quot;quality-compromise&quot;&gt;Quality Compromise &lt;a class=&quot;direct-link&quot; href=&quot;#quality-compromise&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Shortcuts may lead to quicker results, but often at the expense of quality. I see this with code every day. Sure, the work AI spits out often looks impressive, but if you look closer it&#39;s actually very shallow, lacks attention to detail and precision.&lt;/p&gt;
&lt;p&gt;So, keep this in mind when you reach for AI tools in your daily life. Don&#39;t let it take over the things you care about or want to learn. Use it as a sidekick that pokes holes into your own arguments or does brainstorming jam-sessions with you. But don&#39;t let it put in the reps for you.&lt;/p&gt;
&lt;p&gt;I&#39;ll finish this off with the words of Jason and Brian from &lt;a href=&quot;https://gfda.co/&quot;&gt;GFDA&lt;/a&gt;: &amp;quot;A computer is a Lite-Brite for bad fucking ideas&amp;quot; 😁&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do you use AI in your daily life? Do you have any tips on how to not overdo it? Hit me up &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;on the twitters&lt;/a&gt;, I would love to hear more about that!&lt;/strong&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Why I don&#39;t like Streaks</title>
		<link href="https://marcelfahle.net/posts/streaks/"/>
		<updated>2024-05-11T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/streaks/</id>
		<content type="html">&lt;p&gt;My wife once had a Duolingo streak for over 200 days. She was really proud of it and excited to keep learning. Then we had a kid, and no matter how Duolingo helped her with streak freezes and whatnot, she wasn&#39;t able to keep it up.&lt;/p&gt;
&lt;p&gt;The frustration of losing all this accumulated progress, even though it&#39;s just virtual and doesn&#39;t reflect her state of learning, hit her so hard, that she never opened the app again.&lt;/p&gt;
&lt;p&gt;Streaks are stressful&lt;br&gt;
While I like that streaks motivate you, keep you accountable and push you to squeeze another lesson in, no matter what, they can pretty easily backfire. Keeping up can be stressful.&lt;/p&gt;
&lt;p&gt;What to do instead&lt;br&gt;
In meditation I&#39;ve learned the concept to &amp;quot;begin again&amp;quot;. You get distracted? No judgement, no anger. Just start over and begin again.&lt;/p&gt;
&lt;p&gt;I use this concept together with &amp;quot;life tracking&amp;quot;, something I stole from &lt;a href=&quot;https://dyrdekmachine.com/build-with-rob/applying-the-Machine-Mindset-to-automate-every-aspect-of-your-life&quot;&gt;Rob Dyrdek&lt;/a&gt;. He basically tracks all kinds of aspects of his life, keeps a tally or percentage and then just tries to improve his own progress month over month.&lt;/p&gt;
&lt;p&gt;I think this is much more flexible and gentler, than the brutal nature of streaks. With Rob&#39;s words:&lt;/p&gt;
&lt;p&gt;[...] the secret is remaining highly adaptive. [..] being conscious of the fact that life happens, chaos is unavoidable, and you must be able to be flexible within your systems in order for them to continue to function.&lt;/p&gt;
&lt;p&gt;And this is what really did it for me. Like Rob, I now track all kinds of things: journaling, my wake-up times, how many cakes I eat, and more.&lt;/p&gt;
&lt;p&gt;If I mess up a day or two or three, no biggie. Just begin again. Check in at the end of the month and try to be better the next month.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What do you do to keep yourself accountable? I would love to hear your stories. Find me on Twitter at &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;twitter.com/marcelfahle&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Action Beats Insights</title>
		<link href="https://marcelfahle.net/posts/action/"/>
		<updated>2024-05-12T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/action/</id>
		<content type="html">&lt;p&gt;Every day, we gather new insights. We read a quote, listen to a podcast, or have a deep conversation. We feel smarter. But does that make us better? Not really.&lt;/p&gt;
&lt;p&gt;Understanding something doesn&#39;t mean we&#39;re improving. It&#39;s easy to think we&#39;re moving forward when we&#39;re just standing still. Insights feels good. They make us nod and thing, &amp;quot;Yes, I get it.&amp;quot; But without action, they&#39;re like seeds that never sprout.&lt;/p&gt;
&lt;p&gt;Think about what you learned today. Did you use it? If not, it&#39;s just a cool idea. Nothing more. We collect those ideas like gamers collect skins in Fortnite. Pretty to look at, but they don&#39;t get us anywhere.&lt;/p&gt;
&lt;p&gt;Here&#39;s the truth. Actions change things, not ideas. You can know the best ways to lose weight, save money, or be happy. But if you sit on your couch, nothing changes. Knowing isn&#39;t enough. Doing is what matters.&lt;/p&gt;
&lt;p&gt;So, what should you do? Start small. Pick one insight. What&#39;s one thing you can do right now to use it?Maybe it&#39;s making a call you&#39;ve been avoiding. Perhaps it&#39;s writing a page of that book you want to write. Or it could be throwing out the junk food in your fridge.&lt;/p&gt;
&lt;p&gt;Act. Move. Do. That&#39;s how you turn insight into progress. It&#39;s not about feeling smart. And being smart means moving from thought to action.&lt;/p&gt;
&lt;p&gt;Remember, every step counts. Even a small step moves you forward. So, let&#39;s not think about how to live. Let&#39;s live it. Right now.&lt;/p&gt;
&lt;p&gt;If you didn&#39;t find this too corny and it actually put your ass into gear, please &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;hit me up on Twitter&lt;/a&gt; and let me know about it.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>The Power of Cohorts and Community for Learning</title>
		<link href="https://marcelfahle.net/posts/cohorts/"/>
		<updated>2024-05-13T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/cohorts/</id>
		<content type="html">&lt;p&gt;I&#39;m now on Day 7 of &lt;a href=&quot;https://www.ship30for30.com/&quot;&gt;Ship 30 for 30&lt;/a&gt;, and I finally understand &lt;a href=&quot;https://www.skool.com/&quot;&gt;Skool&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/watch?v=IFpHasZ7jN4&quot;&gt;Hormozi&#39;s investment&lt;/a&gt; a lot better.&lt;/p&gt;
&lt;h2 id=&quot;the-problem-of-online-courses&quot;&gt;The Problem of Online Courses &lt;a class=&quot;direct-link&quot; href=&quot;#the-problem-of-online-courses&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s a straight-up fact: only about &lt;a href=&quot;https://oro.open.ac.uk/43566/&quot;&gt;5-15% of people finish their online courses&lt;/a&gt;. Think about that for a second. If a hundred people sign up, maybe just five to fifteen cross the finish line.&lt;/p&gt;
&lt;p&gt;The information alone doesn&#39;t hold attention, and solo learning lacks drive—I see it in my own overflowing Gumroad library. 🙈&lt;/p&gt;
&lt;h2 id=&quot;cue-in-some-accountability-buddies&quot;&gt;Cue in some accountability buddies &lt;a class=&quot;direct-link&quot; href=&quot;#cue-in-some-accountability-buddies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ship 30 on Skool works differently with its cohort model. We start and aim to finish together, transforming us from a group of students into a team.&lt;/p&gt;
&lt;p&gt;And that&#39;s where cohorts and communities make a big difference. They turn learning into a shared journey, not a lonely trek. Communities support each other. They share goals and push each other to reach them.&lt;/p&gt;
&lt;p&gt;Imagine trying to run every morning alone. Now, imagine running with a friend who waits for you every morning. Which one keeps you more motivated?&lt;/p&gt;
&lt;h2 id=&quot;completion-rates-at-least-doubled&quot;&gt;Completion rates at least doubled &lt;a class=&quot;direct-link&quot; href=&quot;#completion-rates-at-least-doubled&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Studies show that &lt;a href=&quot;https://www.edsurge.com/news/2019-06-06-moving-from-5-to-85-completion-rates-for-online-courses&quot;&gt;learning in groups significantly increases completion rates&lt;/a&gt;. For example, &lt;a href=&quot;https://www.youtube.com/watch?v=BMndJYa_KbM&quot;&gt;in a community-focused course setup&lt;/a&gt;, around 36% of students complete their courses. That&#39;s more than double the usual rates!&lt;/p&gt;
&lt;p&gt;So, for me as a learner, the shift from just selling access to information to creating engaging learning communities is a game changer. By building spaces where learners interact, help and hold each other accountable, it is ensured that more students cross the finish line, myself included.&lt;/p&gt;
&lt;p&gt;How many unfinished courses do you own? Hit me up on &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;Twitter&lt;/a&gt; and shoot me your count, so we can compare numbers 🤣&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>How Self-Doubt leads to Procrastination</title>
		<link href="https://marcelfahle.net/posts/selfdoubt/"/>
		<updated>2024-05-14T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/selfdoubt/</id>
		<content type="html">&lt;p&gt;In &amp;quot;Discipline Equals Freedom&amp;quot;, Jocko Willink labels procrastination as laziness. He&#39;s not wrong, but in my own battles, I&#39;ve found it&#39;s often about fear, not laziness.&lt;/p&gt;
&lt;p&gt;Procrastination isn&#39;t just about avoiding work or being messy. It&#39;s our inner voice whispering about the risks of failing, about the rough edges in our work, and sometimes, about the weight of our own potential.&lt;/p&gt;
&lt;h3 id=&quot;self-doubt-kicks-in&quot;&gt;Self-Doubt kicks in &lt;a class=&quot;direct-link&quot; href=&quot;#self-doubt-kicks-in&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Take my project &lt;a href=&quot;https://bold.video/&quot;&gt;Bold&lt;/a&gt;, for example. Each time a sales-meeting is pushed back, a part of me is secretly relieved. Why? Because it means I won&#39;t have to show something that might break or is imperfect, while at the same time praising how awesome it is.&lt;/p&gt;
&lt;p&gt;Yet, here&#39;s the funny part: I have another app, &lt;a href=&quot;https://pidro.online/&quot;&gt;Pidro&lt;/a&gt;, with over 62,000 players, and I talk about its success with ease. It&#39;s odd how confidence in one area doesn&#39;t always spread to others.&lt;/p&gt;
&lt;p&gt;Through writing and connecting with mentors, I&#39;ve confronted the truth behind my delays. Every excuse points to a doubt, a worry that what I&#39;m doing isn’t quite there yet. It&#39;s a story many of us tell ourselves.&lt;/p&gt;
&lt;p&gt;Real power comes from seeing procrastination as our defense against showing imperfections. By acknowledging this, we can begin to drop our guard.&lt;/p&gt;
&lt;h3 id=&quot;how-to-solve-it&quot;&gt;How to solve it &lt;a class=&quot;direct-link&quot; href=&quot;#how-to-solve-it&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;My strategy to beat procrastination:&lt;br&gt;
&lt;strong&gt;Embrace the Flaws&lt;/strong&gt;: Creating, like blogging, is about accepting the imperfect. Every effort is a step forward, not a quest for flawlessness. It&#39;s sort of what I&#39;m currently doing with this blog. 💡&lt;/p&gt;
&lt;p&gt;There&#39;s strength in learning from those who have already been where you&#39;re trying to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Risk the Spotlight&lt;/strong&gt;: But putting my work out there, I&#39;ve practiced risking public failure. It&#39;s scary but freeing.&lt;/p&gt;
&lt;p&gt;I&#39;m starting to see every act of procrastination as a chance to talk to our doubts. We don&#39;t need to quiet the inner critic entirely, but we can learn to let it chatter away in the background while we press on.&lt;/p&gt;
&lt;p&gt;To my fellow creators or Indie Hackers struggling with the same issue: use the extra time not to rest, but to sharpen your ideas. Let&#39;s see procrastination as a guide towards deeper work and self-discovery.&lt;/p&gt;
&lt;p&gt;Seth Godin once said, &amp;quot;Instead of wondering when your next vacation is, maybe you should set up a life you don&#39;t need to escape from.&amp;quot; Let&#39;s make work that excites us, challenges us, and satisfies us so much that we don&#39;t feel the need to put it off.&lt;/p&gt;
&lt;p&gt;Are you struggling with mental road blocks like this? Tell me about it on &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;Twitter&lt;/a&gt; and let&#39;s grab our fears by the balls and let the good stuff follow.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Do Shit You Hate: A Guide for the Comfortable</title>
		<link href="https://marcelfahle.net/posts/doshityouhate/"/>
		<updated>2024-05-15T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/doshityouhate/</id>
		<content type="html">
&lt;p&gt;I remember once telling my sister about an upcoming mountain race I was about to run, to which she responded &quot;awww, you deserve this, go enjoy yourself&quot;, like it&#39;s a cup of fucking ice cream. She didn&#39;t get it. This stuff is hard, and training for it is harder. But that&#39;s exactly why you do it.&lt;/p&gt;

&lt;h2&gt;The Reward of Hard Work&lt;/h2&gt;

&lt;p&gt;When you work on something really hard and then see it coming to life, it feels great. It&#39;s a feeling you can&#39;t buy. Whether it&#39;s training for a race, learning a new skill, or starting a new career, pushing yourself beyond your comfort zone brings deep satisfaction.&lt;/p&gt;

&lt;p&gt;You wake up early. You keep going when you&#39;re tired. Some days, you hate it. But you go out anyway. Consistency is key, and momentum matters.&lt;/p&gt;

&lt;h2&gt;Overcoming and Growing&lt;/h2&gt;

&lt;p&gt;The moment you cross that finish line, everything changes. Your legs ache. Your lungs burn. But amid the pain, there&#39;s an unparalleled feeling of pride and accomplishment. This intense satisfaction comes from doing the hard things, not from staying comfortable.&lt;/p&gt;

&lt;p&gt;Challenging yourself means growth. You learn what you&#39;re made of and build strength and grit. Your achievements stay with you forever. They serve as reminders of your capabilities and sources of enduring pride.&lt;/p&gt;

&lt;p&gt;So, if you&#39;re used to comfort and complacency, try embracing the things you dread. Whether it&#39;s running a marathon, learning a language, or tackling a new project, pushing your limits is invariably worth it. The sense of accomplishment that follows will fuel your future endeavors.&lt;/p&gt;

&lt;h2&gt;Take Action&lt;/h2&gt;

&lt;p&gt;Personally, I learned all of this from running and being a father, but currently try to apply that mindset into other parts of my life.&lt;/p&gt;

&lt;p&gt;If this resonates with you, or if you&#39;re struggling to step out of your comfort zone, I want to hear about it. &lt;a href=&quot;https://twitter.com/marcelfahle&quot; target=&quot;_blank&quot;&gt;Hit me up on Twitter&lt;/a&gt;, tell me your story, or share your challenges. Let’s declare comfort the enemy, together!&lt;/p&gt;

&lt;p&gt;To finish this off, here&#39;s a video of my first marathon, the New York City Marathon in 2008. See that satisfaction in chubby young me at the end? That&#39;s what this is all about.&lt;/p&gt;

&lt;div style=&quot;position: relative; padding-bottom: 56.5%; height: 0;&quot;&gt;&lt;iframe src=&quot;https://demo.bold.video/e/wqjpe&quot; frameborder=&quot;0&quot; webkitallowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; allowfullscreen=&quot;&quot; style=&quot;position: absolute; top: 0; left: 0; width: 100%; height: 100%;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Body Doubling Is The Ultimate Cheat Code for Serial Procrastinators</title>
		<link href="https://marcelfahle.net/posts/bodydoubling/"/>
		<updated>2024-05-16T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/bodydoubling/</id>
		<content type="html">&lt;p&gt;I&#39;ve always been a hardcore procrastinator.&lt;/p&gt;
&lt;p&gt;My mind operated like a dog, chasing shiny objects and distractions left and right, only doing the actual work in the very last second, if even.&lt;/p&gt;
&lt;p&gt;Meditation helped with that, but the ultimate cheat code was body doubling.&lt;/p&gt;
&lt;h2 id=&quot;what-is-body-doubling&quot;&gt;What is Body Doubling &lt;a class=&quot;direct-link&quot; href=&quot;#what-is-body-doubling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s a simple concept: you work with one or more partners, either next to you or online. Their presence alone boosts your focus, making it easier to stay on task and plow through the work. It really works like magic.&lt;/p&gt;
&lt;p&gt;Today, procrastination is basically a thing of the past. I bang out 3-4 hour blocks of deep work, daily and effortlessly. And it feels amazing.&lt;/p&gt;
&lt;h2 id=&quot;the-tools-i-use&quot;&gt;The Tools I Use &lt;a class=&quot;direct-link&quot; href=&quot;#the-tools-i-use&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I currently use two tools that enable body doubling for me&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.thesukha.co/&quot;&gt;The Sukha&lt;/a&gt;: The OG and what introduced me to body doubling in the first place. I use it daily to help me especially with days that are fractured by meetings and other distractions. I start the timer and jump into &amp;quot;The Coffee Shop&amp;quot;, where I see the moving avatars of other folks from around the world chipping away at their tasks. It&#39;s like a beautiful online co-working space, full of great people with similar goals. This gets me into laser-focus mode in no time.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://focusmate.com/&quot;&gt;Focusmate&lt;/a&gt;: It&#39;s a 1-on-1 pairing app I use for my morning deep work sessions from 4am to 7am. You meet with a single partner for 25, 50 or 75 minutes at a time, do affirmations before a session and then do a review at the end to see how it went. I love the intentionality behind this and that I need to know what to work on beforehand.&lt;/p&gt;
&lt;p&gt;So, these are the two tools I use daily and what changed the game for me.&lt;/p&gt;
&lt;p&gt;Are you a procrastinator? Let me know about your battle-tactics &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;on Twitter&lt;/a&gt;, I&#39;m always eager to learn and improve. 🙏&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Quantity Leads to Quality</title>
		<link href="https://marcelfahle.net/posts/quantity/"/>
		<updated>2024-05-17T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/quantity/</id>
		<content type="html">&lt;p&gt;Almost every day, I write and publish an essay. It&#39;s part of a challenge called Ship 30 for 30. Some essays might seem bad, and ideas might look unfinished. That&#39;s okay. The goal is to publish.&lt;/p&gt;
&lt;p&gt;Writing a lot helps me improve. It&#39;s like learning by doing. If I wait for each piece to be perfect, I might never share anything.&lt;/p&gt;
&lt;p&gt;Kent Beck once said &amp;quot;By far the dominant reason for not releasing sooner was a reluctance to trade the dream of success for the reality of feedback.&amp;quot; (&lt;a href=&quot;https://bit.ly/44IIZR1&quot;&gt;source&lt;/a&gt;) I face this fear every day.&lt;/p&gt;
&lt;p&gt;Each essay is a chance to play with ideas. They don&#39;t have to be complete or perfect. Every piece I write is a step towards better writing. It&#39;s about getting the thoughts out there, not crafting masterpieces overnight.&lt;/p&gt;
&lt;p&gt;This daily practice turns my thoughts into words quickly. It forces me to think on my feet and improve my skills. The more I write, the more I can say. This method might be messy, but it&#39;s effective. It keeps me moving forward and builds momentum.&lt;/p&gt;
&lt;p&gt;In the end, it&#39;s not about the perfect essay (or software). It&#39;s about the act of writing and improving through quantity. Every published piece is progress.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Why downplaying your skills is a shield against failure</title>
		<link href="https://marcelfahle.net/posts/downplaying/"/>
		<updated>2024-05-19T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/downplaying/</id>
		<content type="html">&lt;p&gt;For years, I&#39;ve called myself a &amp;quot;junior developer for life.&amp;quot; What started as a humble acknowledgment of always having something to learn has become something else entirely. Through (almost) daily writing, I&#39;ve noticed a deeper reason behind this label.&lt;/p&gt;
&lt;h3 id=&quot;the-real-reason-behind-the-label&quot;&gt;The real reason behind the label &lt;a class=&quot;direct-link&quot; href=&quot;#the-real-reason-behind-the-label&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Calling myself a junior developer has been a subconscious shield. It protects me from the risk of failure. If I undersell myself, I don&#39;t have to worry about not meeting high expectations. It&#39;s a safe space where I can always claim I&#39;m still learning and never quite there yet.&lt;/p&gt;
&lt;p&gt;Initially, I thought this mindset was rooted in humility and a genuine curiosity to keep learning. But as I explored my thoughts through writing, I realized it was more about avoiding the pressure of higher standards. By keeping the bar low, I shield myself from the fear of not matching it.&lt;/p&gt;
&lt;p&gt;Writing has helped me see this pattern. It&#39;s not just about being humble; it&#39;s about a fear of vulnerability (Hello, &lt;a href=&quot;https://brenebrown.com/book/daring-greatly/&quot;&gt;Brené Brown&lt;/a&gt; 👋). Admitting I have twenty years of experience and calling myself a senior or staff engineer means I&#39;m accountable to that title. It means facing the possibility of falling short.&lt;/p&gt;
&lt;h3 id=&quot;embracing-my-true-self&quot;&gt;Embracing my true self &lt;a class=&quot;direct-link&quot; href=&quot;#embracing-my-true-self&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now, I see the need to shed this protective label. It&#39;s time to embrace my experience and the skills I&#39;ve honed over two decades. It&#39;s time to take pride in my journey and be willing to stand up to the expectations that come with it.&lt;/p&gt;
&lt;p&gt;Have you faced similar mental blockers? Tell me about it on &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;Twitter&lt;/a&gt;, I&#39;m always eager to learn how others discover and tackle their mental demons.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Things I&#39;ve learned from running</title>
		<link href="https://marcelfahle.net/posts/runninglessons/"/>
		<updated>2024-05-22T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/runninglessons/</id>
		<content type="html">&lt;p&gt;To me, running is more than just a way to stay healthy. It has taught me valuable lessons that I, over time, started to apply to my professional life and other areas. Here are my favorite ones:&lt;/p&gt;
&lt;h3 id=&quot;measure-progress&quot;&gt;Measure Progress &lt;a class=&quot;direct-link&quot; href=&quot;#measure-progress&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Running is easy to measure. You log kilometers, heart rate, and times. You see if you improve.&lt;/p&gt;
&lt;p&gt;At work, progress is harder to see. So, I keep a bullet journal to track wins and losses. Like logging kilometers, it helps me see growth and stay motivated.&lt;/p&gt;
&lt;h3 id=&quot;stick-to-the-plan&quot;&gt;Stick to the Plan &lt;a class=&quot;direct-link&quot; href=&quot;#stick-to-the-plan&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many days, I don&#39;t want to run. I started putting my runs on the calendar and go anyway. When it&#39;s on the calendar, I don&#39;t ask if I want to go. I just go.&lt;/p&gt;
&lt;p&gt;I started applying this habit to other parts of my life. Even for fun stuff. In fact, I got one of &lt;a href=&quot;https://www.youtube.com/watch?v=HineqycOw98&quot;&gt;Jesse Itzler&#39;s bigass calendars&lt;/a&gt; this year, and love it. Putting things on my calendar is sort of a mini contract with myself.&lt;/p&gt;
&lt;h3 id=&quot;focus-on-consistency&quot;&gt;Focus on Consistency &lt;a class=&quot;direct-link&quot; href=&quot;#focus-on-consistency&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As with every sport, the key is to stay consistent. Every run, especially the hard ones, builds endurance, strength, and discipline.&lt;/p&gt;
&lt;p&gt;It&#39;s the same in life. Consistent effort, even in small amounts, leads to big results. It&#39;s not about one big push, but steady, ongoing work.&lt;/p&gt;
&lt;h3 id=&quot;learn-from-every-run&quot;&gt;Learn from Every Run &lt;a class=&quot;direct-link&quot; href=&quot;#learn-from-every-run&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each run teaches me something. Some days are easy, some are tough. I learn about my limits, my strengths, and how to push through challenges.&lt;/p&gt;
&lt;p&gt;This applies to all areas of life. Every task, even small ones, offers lessons. Pay attention and embrace each experience as an opportunity to grow and improve.&lt;/p&gt;
&lt;p&gt;Running has changed my life. It taught me to measure progress, stick to the plan, stay consistent, and learn from each step. These habits have made me more disciplined, reliable, and brought me closer to my goals. Small steps lead to big changes, and running is a perfect example of that.&lt;/p&gt;
&lt;p&gt;Hit me up on &lt;a href=&quot;https://www.strava.com/athletes/18230818&quot;&gt;Strava&lt;/a&gt;, so we can run and grow together!&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Don&#39;t emulate people at their peak, do what they did at the start</title>
		<link href="https://marcelfahle.net/posts/earlydays/"/>
		<updated>2024-05-24T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/earlydays/</id>
		<content type="html">&lt;p&gt;In high school, Kobe Bryant showed up at practice at 5 AM.&lt;/p&gt;
&lt;p&gt;It&#39;s great to get inspired by your idols at their peak, but don&#39;t emulate their current habits. Instead, follow what they did at the start, when they were in a situation like yours.&lt;/p&gt;
&lt;h3 id=&quot;kobe-bryant&#39;s-early-mornings&quot;&gt;Kobe Bryant&#39;s early mornings &lt;a class=&quot;direct-link&quot; href=&quot;#kobe-bryant&#39;s-early-mornings&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Kobe Bryant spent countless hours practicing in high school. He practiced shots alone and worked tirelessly. This hard work built his skills and confidence. It set the stage for his future success. Emulating his early dedication is key, not just the peak routines.&lt;/p&gt;
&lt;h3 id=&quot;steve-jobs&#39;-early-hustle&quot;&gt;Steve Jobs&#39; early hustle &lt;a class=&quot;direct-link&quot; href=&quot;#steve-jobs&#39;-early-hustle&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the early days of Apple, Steve Jobs made cold calls to investors. He pitched his ideas tirelessly. His early hustle was key to Apple&#39;s success. Aspiring entrepreneurs should mimic his early efforts, not the later strategies.&lt;/p&gt;
&lt;h3 id=&quot;sara-blakely&#39;s-persistence&quot;&gt;Sara Blakely&#39;s persistence &lt;a class=&quot;direct-link&quot; href=&quot;#sara-blakely&#39;s-persistence&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sara Blakely, the founder of Spanx, pitched her product to stores herself. She faced rejection but kept going. She wore Spanx in meetings to show their value. Her early persistence was crucial. Emulate her drive, not her later success.&lt;/p&gt;
&lt;h3 id=&quot;learning-from-everyday-heroes&quot;&gt;Learning from Everyday Heroes &lt;a class=&quot;direct-link&quot; href=&quot;#learning-from-everyday-heroes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Coming back to the real world, I personally enjoy the early stories of entrepreneurs like &lt;a href=&quot;https://justinjackson.ca/&quot;&gt;Justin Jackson&lt;/a&gt; and &lt;a href=&quot;https://jonbuda.com/&quot;&gt;Jon Buda&lt;/a&gt; (&lt;a href=&quot;https://transistor.fm/&quot;&gt;Transistor&lt;/a&gt;), &lt;a href=&quot;https://uibreakfast.com/&quot;&gt;Jane Portman&lt;/a&gt; and &lt;a href=&quot;https://benediktdeicke.com/&quot;&gt;Benedikt Deicke&lt;/a&gt; (&lt;a href=&quot;https://userlist.com/&quot;&gt;Userlist&lt;/a&gt;), and &lt;a href=&quot;https://robwalling.com/&quot;&gt;Rob Walling&lt;/a&gt; and &lt;a href=&quot;https://www.derrickreimer.com/&quot;&gt;Derrick Reimer&lt;/a&gt; (&lt;a href=&quot;https://www.drip.com/&quot;&gt;Drip&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;They documented their journeys and early struggles with their podcasts [&lt;a href=&quot;https://productpeople.tv/episodes?page=4&amp;amp;per=30&quot;&gt;1&lt;/a&gt;], [&lt;a href=&quot;https://www.slowandsteadypodcast.com/episodes?page=7&amp;amp;per=30&quot;&gt;2&lt;/a&gt;], [&lt;a href=&quot;https://www.startupsfortherestofus.com/archives&quot;&gt;3&lt;/a&gt;] and articles. These stories show that success starts with small steps, persistence, and learning.&lt;/p&gt;
&lt;p&gt;Whether looking at Bryant, Jobs, Blakely, or my favorite tech entrepreneurs, the lesson is clear: start small, stay persistent, and learn. Success begins at the start, not the peak.&lt;/p&gt;
&lt;p&gt;Do you have other cool examples of &amp;quot;well documented early days&amp;quot; that I can learn from? Please let me know about it on &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>The Illusion of ease: Mastery makes it look easy</title>
		<link href="https://marcelfahle.net/posts/mastery/"/>
		<updated>2024-05-27T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/mastery/</id>
		<content type="html">
&lt;p&gt;Yesterday, I watched Kilian Jornet run into his 11th Victory in Zegama. He makes running up Sancti Espiritu look easy. It isn&#39;t.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/Jz9tL5dcZkE?si=98VYk07BJt3zDOlA&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Years of practice&lt;/h2&gt;

&lt;p&gt;Masters practice for years. Steph Curry shoots hoops daily, hour after hour. His perfect shots come from countless hours in the gym, turning skills into muscle memory.&lt;/p&gt;

&lt;h2&gt;The power of persistance&lt;/h2&gt;

&lt;p&gt;Mental strength plays a key role. Masters push through plateaus and tough times. They train even when they don&#39;t feel like it. This persistence transforms their skills into excellence.&lt;/p&gt;

&lt;h2&gt;Don&#39;t be fooled&lt;/h2&gt;

&lt;p&gt;When we see masters, their grace misleads us. The ease we see hides hard work, discipline, and passion. Excellence takes effort. Next time you see a master, remember the unseen effort behind their skill. Let their achievements inspire you. &lt;/p&gt;

&lt;h2&gt;Push through and keep learning&lt;/h2&gt;

&lt;p&gt;Masters like Kilian Jornet show that hard work and dedication make the difficult look easy. Their success comes from relentless effort and strong will. Don&#39;t be discouraged by the hard parts. Push through, keep learning and stay dedicated. The journey to mastery is long but rewarding. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who inspires you and makes their craft look easy? Let me know about it on &lt;a href=&quot;https://twitter.com/marcelfahle&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt;, I want to know all about them!&lt;/strong&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>BOLD Spark ⚡</title>
		<link href="https://marcelfahle.net/posts/bold-spark/"/>
		<updated>2024-05-28T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/bold-spark/</id>
		<content type="html">&lt;p&gt;Bold is not a huge app by any means. You can upload or import videos, organize them, share them. That&#39;s the essence.&lt;/p&gt;
&lt;p&gt;But to get started, setting up your video portal, organizing playlists, setting up menus, etc., it can still overwhelm. I think.&lt;/p&gt;
&lt;p&gt;That&#39;s why I was toying for a long time now with some sort of &amp;quot;Mini Bold&amp;quot;: You upload a video, get a link or an embed code back, see some stats. That&#39;s it.&lt;/p&gt;
&lt;p&gt;I think this could be great for Landing Page owners, documentation or just one off videos that you want to share with a small group of people.&lt;/p&gt;
&lt;p&gt;I was working a bit on this idea over the last month and decided to build it. I will call it Bold Spark⚡.&lt;/p&gt;
&lt;p&gt;Initially I will keep this free to use and see how it goes. The main idea is that it might be a valuable tool that has the potential to get shared a lot and then drive traffic to Bold.&lt;/p&gt;
&lt;p&gt;I will report back in a month or so. In the mean time, if you think this is useful, you could use this, or of you think this is total horseshit, &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;hit me up on Twitter&lt;/a&gt;. I&#39;d love to hear all about it :)&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Indiscrimiate Action is Laziness</title>
		<link href="https://marcelfahle.net/posts/indiscriminate-action/"/>
		<updated>2024-06-04T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/indiscriminate-action/</id>
		<content type="html">&lt;p&gt;Indiscriminate action is laziness.&lt;/p&gt;
&lt;p&gt;Tim Ferriss said that, &lt;a href=&quot;https://tim.blog/2013/11/03/productivity-hacks/&quot;&gt;sort of&lt;/a&gt;, and it hit me hard. I used to navigate my tasks like a squirrel in traffic, darting from one thing to another without thought. Whatever gives me the hardest dopamine hit, gets worked on. But that&#39;s wrong.&lt;/p&gt;
&lt;p&gt;Indiscriminate action means doing tasks without purpose or priority. It&#39;s like watering a garden by spraying water everywhere, hoping something will grow. Real productivity comes from focused effort, not random busyness. Tony Robbins also nailed it when he said, &lt;a href=&quot;https://x.com/TonyRobbins/status/796837797982183424&quot;&gt;&amp;quot;Most people fail in life because they major in minor things.&amp;quot;&lt;/a&gt; Focusing on trivial tasks leads nowhere.&lt;/p&gt;
&lt;p&gt;Taking focused action requires mental effort. You need to analyze tasks, plan thoughtful and prioritze, and make strategic decisions about time and resources. Indiscriminate action often avoids this discomfort. It’s easier to do lots of small, unimportant tasks than to face a few big, challenging ones. This avoidance can feel like laziness because it dodges deeper thinking and responsibility.&lt;/p&gt;
&lt;p&gt;To change this, I recently started doing monthly reviews and planning sessions. Here&#39;s how I&#39;m trying to make it work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Set Clear Goals&lt;/strong&gt;: I write down what I want to achieve.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prioritize Tasks&lt;/strong&gt;: I pick tasks that move me closer to my goals and focus on those first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan My Month&lt;/strong&gt;: I break down big goals into smaller tasks and schedule them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This approach helps me give my actions purpose. I feel less overwhelmed and more directed. I still stumble and fall into old habits, but I’m getting better.&lt;/p&gt;
&lt;p&gt;Tim Ferriss&#39;s words pushed me to rethink my strategy. I stopped mistaking busyness for productivity. Now, I aim for focused, meaningful work. It&#39;s a journey, but with clear goals and priorities, I&#39;m starting to find my way.&lt;/p&gt;
&lt;p&gt;Try it for yourself. Do a monthly review. Set goals. Prioritize. Plan. See if focused action helps you too.&lt;/p&gt;
&lt;p&gt;Does that resonate or am I the only one? Let me know on the &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;Twitters&lt;/a&gt;. I&#39;d love to hear your thoughts.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>July 2024 Recap</title>
		<link href="https://marcelfahle.net/posts/july2024/"/>
		<updated>2024-08-01T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/july2024/</id>
		<content type="html">
&lt;p&gt;
    Over the last two years, while I was evolving my sleep and work habits, 
    I got more serious about taking notes and tracking all sorts of data. 
&lt;/p&gt;
&lt;p&gt;
    I also got into the habit of tracking my daily wins and then summarizing those at 
    the end of the month. It&#39;s not only a great tool to help you zoom out, but also builds momentum.&lt;/p&gt; 
    
&lt;p&gt;When you feel that the daily grind is not 
    getting you anywhere, and then you look at this longass list at the end of the month,
    it lights a huge fire under your ass!
&lt;/p&gt;

&lt;p&gt;
    So, and to push this a bit further, I&#39;m going to try something new. I will try 
    to publish every month or so all the things I did, learned, built, etc. All to please 
    the mighty accountability gods and help to keep an eye on the big picture. My hope is by putting 
    more of my work and thoughts out there, it forces me to think harder about what to 
    work on - or you all going to laugh at me. 😄
&lt;/p&gt;

&lt;p&gt;
    So, without further ado, here&#39;s what was going on July of 2024.
&lt;/p&gt;

    &lt;h3&gt;Stats&lt;/h3&gt;
    &lt;ul class=&quot;stats&quot;&gt;
        &lt;li&gt;&lt;strong&gt;Running:&lt;/strong&gt; 181KM &lt;small&gt;&lt;br&gt;not much incline, we&#39;re in Lithuania for the summer&lt;/small&gt;&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Wake Up:&lt;/strong&gt; 04:28 &lt;small&gt;&lt;br&gt;we switched timezones, the rest of the year is 03:37&lt;/small&gt;&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Sleep:&lt;/strong&gt; 6:12h&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Resting HR:&lt;/strong&gt; 47.24&lt;/li&gt;
    &lt;/ul&gt;
    &lt;h3&gt;&lt;a href=&quot;https://bold.video/&quot; target=&quot;_blank&quot;&gt;Bold&lt;/a&gt;&lt;/h3&gt;
        &lt;ul class=&quot;stats&quot;&gt;
            &lt;li&gt;
                &lt;strong&gt;Sortable Playlists&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Can&#39;t believe it took me this long to get this in.&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Improved Uploader&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Now uploads can be resumed, interrupted, deal with messy internet, etc.&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;New Landing Page&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;This is still a work in progress but I&#39;m happy with where we are. I&#39;m grateful 
                for my good friends at &lt;a href=&quot;https://thefeeling.de/&quot; target=&quot;_blank&quot;&gt;The Feeling&lt;/a&gt; for helping me with this! ❤️&lt;/small&gt;
                &lt;div class=&quot;img-wrapper&quot;&gt;
                    &lt;figure&gt;
                        &lt;img src=&quot;https://marcelfahle.net/img/blog/bold-redesign-draft.jpg&quot; alt=&quot;Lots of iterations&quot;&gt;
                        &lt;figcaption&gt;Tons of iterations&lt;/figcaption&gt;
                    &lt;/figure&gt;
                &lt;/div&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Added Transcripts to Google Meet/Zoom&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Videos ingested from Google or Zoom didn&#39;t get sent to the transcription service. 
                Now they do! 💪&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Small Updates to &lt;a href=&quot;https://www.npmjs.com/package/@boldvideo/bold-js&quot; target=&quot;_blank&quot;&gt;bold-js&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;&lt;small&gt;published 0.3.5&lt;/small&gt;&lt;br&gt;
            &lt;/li&gt;   
            &lt;li&gt;
                &lt;strong&gt;Bold Spark ⚡&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;&lt;a href=&quot;https://marcelfahle.net/posts/bold-spark&quot;&gt;Talked about it last month&lt;/a&gt; and made some good strides on the MVP this month. Will probably launch it in August.&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Bold Portal&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Bold&#39;s self-service Video Portal. Coming along great. Went deep on fast rendering with React Server Components and Next.js and added a Theme Switcher. &lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Zoom Marketplace renewal&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Had to renew the &lt;a href=&quot;https://marketplace.zoom.us/apps/XCVVJGmSRju8-WLbzMw18A&quot; target=&quot;_blank&quot;&gt;Zoom Marketplace app&lt;/a&gt;. A bit of a pain todo, but necessary and it got accepted right away!&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Outreach&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Still testing the waters on this one, but sent 60 emails and had some interesting conversations.&lt;/small&gt;
            &lt;/li&gt;
        &lt;/ul&gt;
    &lt;h3&gt;&lt;a href=&quot;https://pidro.online/&quot; target=&quot;_blank&quot;&gt;Pidro&lt;/a&gt;&lt;/h3&gt;
    &lt;ul class=&quot;stats&quot;&gt;
        &lt;li&gt;
            &lt;strong&gt;Added data to &lt;a href=&quot;https://posthog.com/&quot; target=&quot;_blank&quot;&gt;Posthog&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;We&#39;re currently ramping up our marketing efforts and as we all know, &quot;You can&#39;t improve what you don&#39;t measure&quot;. I love how this turned out!&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Deployed API v3&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Now with tighter security! 🔒&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Got Monika (wifey! ❤️) onboarded and she sent her first email campaign&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;She&#39;s a natural! 💪&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Introduced regular debugging sessions&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;There are still so many issues that pop up every now and then. This is a dedicated timeslot to get to the bottom of them.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Migrated some more functions off of API v1&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;It&#39;s a long story (worth a blogpost), but in short, there are still some essential functions running on API v1.
            I migrated a few big ones this month to Elixir-land. 🥳&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Facebook Login is back&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Huge pain in the ass! Our Facebook app was in some kind of verification limbo. No moving forward.
            I had to create a new one to get this sorted. Works now!&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Shipped Pidro 2.3.7 to the app stores&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;A few bugfixes and the FB login, now live on the App Store and Google Play. 🍾&lt;/small&gt;
        &lt;/li&gt;
    &lt;/ul&gt;
    &lt;p&gt;
        While there are a lot of exciting things going at my dayjob, I can&#39;t talk about most of these things, 
        so I&#39;ll leave that out. But, it has been a pretty exciting month there as well 🚀
    &lt;/p&gt;
    &lt;p&gt;
        Well, and that&#39;s it. The focus in August will be for Bold mainly on outreach and Bold Spark and Portal. 
        And on the Pidro side, we will keep creating fun email sequences with &lt;a href=&quot;https://loops.so/&quot; target=&quot;_blank&quot;&gt;Loops&lt;/a&gt; 
        and also switch to &lt;a href=&quot;https://revenuecat.com/&quot; target=&quot;_blank&quot;&gt;RevenueCat&lt;/a&gt; for our paywall.
        &lt;br&gt;
        &lt;br&gt;
        Let&#39;s see how it goes.💪 
    &lt;/p&gt;



</content>
	</entry>
	
	<entry>
		<title>August &amp; September 2024 Recap</title>
		<link href="https://marcelfahle.net/posts/august-september2024/"/>
		<updated>2024-10-01T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/august-september2024/</id>
		<content type="html">
&lt;p&gt;
    I skipped the August recap, because we did the multi-day drive from Lithuania back home to Spain.
    It took a few days to get back into the swing of things, but I think we&#39;re back on track now.
&lt;/p&gt;
&lt;p&gt;
    Not having the in-laws around to help with our monkeys meant that &quot;hobbies&quot; like running took quite a hit
    in September. I&#39;m still trying to figure out how to balance it and think I&#39;ve found a good schedule that
    might get me around 6h of running a week. We&#39;ll see.
&lt;/p&gt;

&lt;p&gt;
    Anyway, here&#39;s what was going on in August and September of 2024.
&lt;/p&gt;

    &lt;h3&gt;Stats August&lt;/h3&gt;
    &lt;ul class=&quot;stats&quot;&gt;
        &lt;li&gt;&lt;strong&gt;Running:&lt;/strong&gt; 210KM &lt;small&gt;&lt;br&gt;no incline, still in Lithuania&lt;/small&gt;&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Wake Up:&lt;/strong&gt; 03:56&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Sleep:&lt;/strong&gt; 5:47h&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Resting HR:&lt;/strong&gt; 44.82&lt;/li&gt;
    &lt;/ul&gt;
    &lt;p&gt;
        I also ran that 42km route that &lt;a href=&quot;https://marcelfahle.net/posts/running-2024/&quot;&gt;I talked about in February&lt;/a&gt;. The last 10km weren&#39;t fun, but I&#39;m glad I went for it.
    &lt;/p&gt;

    &lt;h3&gt;Stats September&lt;/h3&gt;
    &lt;ul class=&quot;stats&quot;&gt;
        &lt;li&gt;&lt;strong&gt;Running:&lt;/strong&gt; 98KM / 2600m+ &lt;small&gt;&lt;br&gt;not much running, but finally some climbs 🥳&lt;/small&gt;&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Wake Up:&lt;/strong&gt; 03:44&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Sleep:&lt;/strong&gt; 5:49h&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Resting HR:&lt;/strong&gt; 47.21&lt;/li&gt;
    &lt;/ul&gt;
    &lt;p&gt;
        Sleep quality definitely suffered a bit in September because of the heat and humidity we still have here. But it&#39;s slowly getting better.
    &lt;/p&gt;
    &lt;h3&gt;&lt;a href=&quot;https://bold.video/&quot; target=&quot;_blank&quot;&gt;Bold&lt;/a&gt;&lt;/h3&gt;
        &lt;ul class=&quot;stats&quot;&gt;
        &lt;li&gt;
                &lt;strong&gt;New Logos added&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;The &lt;a href=&quot;https://alfa-omega.org/&quot; target=&quot;_blank&quot;&gt;Alfa y Omega&lt;/a&gt; school in Dénia and Rob Hope&#39;s new Landing page course &lt;a href=&quot;https://showthem.com/&quot; target=&quot;_blank&quot;&gt;showthem.com&lt;/a&gt; are now running on Bold 🥳&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;New Landing Page launched&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Still incomplete and tons of issues, especially the responsive parts, but it&#39;s a start, and better what we had before. &lt;a href=&quot;https://bold.video/&quot; target=&quot;_blank&quot;&gt;Check it out!&lt;/a&gt;&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Bold Portal launched&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Self-service video portal builder. This was a big one and it&#39;s cool. I run this currently in closed beta and will write more about it soon.&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Sent 250 outreach emails&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;55% open rate, a bunch of valuable insights and conversations. Nothing closed yet though.&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Tested small ad campaign&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Dumped so far &amp;euro;700 into this, 80k impressions, 4k clicks, 40 signups (mostly duds). Lots to learn still 🤓&lt;/small&gt;
            &lt;/li&gt;

            &lt;li&gt;
                &lt;strong&gt;Feature Flags&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;Bold has feature flags now. Feels pretty satisfying to turn features on and off per customer 😁&lt;/small&gt;
            &lt;/li&gt;

            &lt;li&gt;
                &lt;strong&gt;Launched Mission Control&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;I built a small super admin tool to help me with administration. I used Cursor for this. The productivity is insane, but boy do I hate vscode 🙈&lt;/small&gt;
            &lt;/li&gt;
            &lt;li&gt;
                &lt;strong&gt;Bold Spark ⚡ goes free tier&lt;/strong&gt;&lt;br&gt;
                &lt;small&gt;As I was working on &lt;a href=&quot;https://marcelfahle.net/posts/bold-spark&quot;&gt;Spark&lt;/a&gt; I came to the conclusion that, instead of it being another app, like a lead magnet, it could just be a free tier on Bold for bootstrappers. That&#39;s where I&#39;m currently at with this but it&#39;s still developing.&lt;/small&gt;
            &lt;/li&gt;
        &lt;/ul&gt;
    &lt;h3&gt;&lt;a href=&quot;https://pidro.online/&quot; target=&quot;_blank&quot;&gt;Pidro&lt;/a&gt;&lt;/h3&gt;
    &lt;ul class=&quot;stats&quot;&gt;
        &lt;p&gt;Pidro was riddled with problems and I was mostly in firefighting mode. It all comes down to a slow feedback loop with an external developer and
        difficulty debugging the unity client. Change needs to happen.&lt;/p&gt;
        &lt;li&gt;
            &lt;strong&gt;Integrated Revenuecat&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Not completely done yet, the paywall is still missing. But time&#39;s ticking, black friday is near.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Met with &lt;a href=&quot;https://x.com/stevepyoung&quot; target=&quot;_blank&quot;&gt;Steve P. Young&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Steve gave me tons of valuable input on Pidro and got me excited about its potential.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Goldcards are back&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Took us a while to get this on right because of a new ad network we&#39;re using. I was surprised about how popular this feature is.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;New Shuffling Algo&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;This was prompted by user feedback, who thought we&#39;re manipulating the dealing to her disadvantage. That&#39;s of course not
            true but it got me to improve the shuffling and implement the &lt;a href=&quot;https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle&quot; target=&quot;_blank&quot;&gt;Fisher-Yates algorithm&lt;/a&gt;&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Migrated the Unity Client to API 3&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;This caused most of the issues. We have an updated token based auth which forced all users to log out.
            But lots of users don&#39;t remember their usernames or emails they used for Pidro when they signed up 5 years ago. So this resulted in lots and lots of support&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Monika is doing now most of the support&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;The fact that she keeps an eye on things and me in check when I&#39;m needed is invaluable.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Shipped Pidro 2.3.8 and 2.3.9 to the app stores&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;But as said, mostly in firefighting mode.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Started working on Pidro 2&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Currently split in 2 Elixir apps (game engine and api) and a react native client. The idea is to get rid of our unity client.
            I don&#39;t have much time for working on this but I broke it down into small tasks that I can chip away on it whenever I have an hour or two. I&#39;m still trying to find a good,
            regular timeslot for this in the week to apply some constistency. Let&#39;s see how October goes.&lt;/small&gt;
        &lt;/li&gt;
    &lt;/ul&gt;
    &lt;p&gt;
        Again, tons of of exciting things going at my dayjob, but unfortunately I can&#39;t talk about most of these. It has been a pretty exciting though 🚀
    &lt;/p&gt;
    &lt;p&gt;
        Well, and that&#39;s it. The focus in October will be for Bold a proper demo and getting bettter outreach.
        I also may or may not have applied to Tinyseed and hooked up with Dan Martell and his team. Let&#39;s see how that goes. 🙂
        As for Pidro, I need to get the paywall up and running and bang out some bugs in the unity client.
        &lt;br&gt;
        &lt;br&gt;
        Let&#39;s keep moving forward 🚀
    &lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>The Smarter You Feel, the Less You Grow</title>
		<link href="https://marcelfahle.net/posts/too-smart/"/>
		<updated>2024-11-01T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/too-smart/</id>
		<content type="html">&lt;p&gt;Feeling smart is great. You&#39;ve worked hard, picked up skills, and now you&#39;re confident in what you do. But here&#39;s the hidden cost: that confidence can become your biggest roadblock. When you feel like you “know it all,” the drive to improve fades. You start coasting, relying on what you already know, and you miss the chance to grow.&lt;/p&gt;
&lt;p&gt;That feeling of mastery tricks you. It’s comfortable, sure, but it makes you overlook new insights, avoid challenges, and stick to routines. When confidence turns to complacency, quality drops, ideas get stale, and creativity dries up. You think you’re still on top, but the truth? You’re falling behind.&lt;/p&gt;
&lt;p&gt;So, how do we keep moving forward? Stay curious. Let yourself be a learner again. The smartest move isn’t feeling like you’ve made it; it’s realizing there’s always more to learn. Growth is like running—it only counts if you’re still moving.&lt;/p&gt;
&lt;p&gt;If this hit home, hit me up on &lt;a href=&quot;https://bsky.app/profile/marcelfahle.net&quot;&gt;Bluesky&lt;/a&gt; or &lt;a href=&quot;https://twitter.com/marcelfahle&quot;&gt;Twitter&lt;/a&gt; — I’d love to hear what you think.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Recap October 2024</title>
		<link href="https://marcelfahle.net/posts/october2024/"/>
		<updated>2024-11-07T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/october2024/</id>
		<content type="html">
&lt;p&gt;
    October was a real banger, with a few significant updates (I hope). Let&#39;s go:
&lt;/p&gt;

    &lt;h3&gt;Stats October&lt;/h3&gt;
    &lt;ul class=&quot;stats&quot;&gt;
        &lt;li&gt;&lt;strong&gt;Running:&lt;/strong&gt; 131KM / 2160m+ &lt;small&gt;&lt;br&gt;Spend some time traveling, so less time to run.&lt;/small&gt;&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Wake Up:&lt;/strong&gt; 03:43&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Sleep:&lt;/strong&gt; 5:45h&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Resting HR:&lt;/strong&gt; 45.69&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;
        Been hyper consistent with my wake-up times. Only the three mornings I was away I woke at 4:41, 6:10 😳 and 5:30. Otherwise, on the dot 3:30, every single time.
    &lt;/p&gt;
    &lt;h3&gt;&lt;a href=&quot;https://bold.video/&quot; target=&quot;_blank&quot;&gt;Bold&lt;/a&gt;&lt;/h3&gt;
        &lt;ul class=&quot;stats&quot;&gt;
        &lt;li&gt;
            &lt;strong&gt;TinySeed&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Got rejected. No biggie, I would&#39;ve done the same. My GTM is still full of holes. See you in Spring! 👋&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;I joined &lt;a href=&quot;https://saasacademy.com/&quot; target=&quot;_blank&quot;&gt;SaaS Academy&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;A significant investment, but it&#39;ll be worth it. I will talk more about it in the future.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Hung out with &lt;a href=&quot;https://robhope.com/&quot; target=&quot;_blank&quot;&gt;Rob Hope&lt;/a&gt; in London&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;He was there for Webflow Conf and let me crash in his Airbnb to hang out, &lt;a href=&quot;https://x.com/marcelfahle/status/1847899538347987390&quot; target=&quot;_blank&quot;&gt;Ted Lasso style&lt;/a&gt;, did some &lt;a href=&quot;https://x.com/marcelfahle/status/1847942496761909566&quot; target=&quot;_blank&quot;&gt;running&lt;/a&gt; and some grinding some on &lt;a href=&quot;https://showthem.com/&quot; target=&quot;_blank&quot;&gt;his course&lt;/a&gt;.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Launched Bold AI Assistant &quot;Anton&quot; - &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7254112536059412480/&quot; target=&quot;_blank&quot;&gt;check it out&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Got the idea from one of our customers, Anton. Rolled it out to most customers.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Launched Attachments for Videos&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Didn&#39;t talk about it publicly yet but you can now add slides, pdfs and stuff to videos.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Alfa y Omega went live&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;The school that I&#39;ve onboarded last month went already live a few days later. They use bold Portal! 😎&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;A few more leads and onboarded 3 more, &quot;free trials&quot;&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Both mostly from my extended network. But I&#39;m grateful, even if doesn&#39;t mean anythng for my MRR. But the learnings are gold for me.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Updated a few (headless) customer video portals&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;To make sure they all have access to the new toys (AI, attachments, etc)&lt;/small&gt;
        &lt;/li&gt;

        &lt;/ul&gt;
    &lt;h3&gt;&lt;a href=&quot;https://pidro.online/&quot; target=&quot;_blank&quot;&gt;Pidro&lt;/a&gt;&lt;/h3&gt;
    &lt;ul class=&quot;stats&quot;&gt;
        &lt;p&gt;Not as much progress as I hoped, but taught myself some Unity to be able to move faster in general.&lt;/p&gt;
        &lt;li&gt;
            &lt;strong&gt;Released a client update, 2.3.10&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;We had an issue with Ads in the lobby, which is fixed now&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Pidro HQ Updates&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Added new auth and a ton of tools to our admin tool, PidroHQ. Most of those tools are for my wife, who&#39;s doing support right now. Support-ticket-driven development 😁&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;New Paywall&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Getting ready for black friday. Being able to do changes in Unity myself makes this so much faster.&lt;/small&gt;
        &lt;/li&gt;


    &lt;/ul&gt;
    &lt;p&gt;
        I also went to the &lt;a href=&quot;https://commercetools.com/&quot; target=&quot;_blank&quot;&gt;commercetools&lt;/a&gt; retreat in Valencia for a few days. It was great to see the team again in person!
    &lt;/p&gt;
    &lt;p&gt;
        Well, and that&#39;s it. The focus in November will be going all in at SaaS Academy with my coaches and for Bold once again to get a proper demo out and get better at outreach.
        As for Pidro, Black Friday is upon us and I want to run a few deals on our in-app purchases. That&#39;ll be the first update on the unity client that I did by myself. Let&#39;s see!
    &lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Recap November 2024</title>
		<link href="https://marcelfahle.net/posts/nov2024/"/>
		<updated>2024-12-08T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/nov2024/</id>
		<content type="html">
&lt;p&gt;
    November was a solid month. Nothing crazy flashy, but the kind of work that sets the stage for bigger things. I’m pretty hyped about what’s coming next.
&lt;/p&gt;

    &lt;h3&gt;Stats October&lt;/h3&gt;
    &lt;ul class=&quot;stats&quot;&gt;
        &lt;li&gt;&lt;strong&gt;Running:&lt;/strong&gt; 99KM / 2760m+ &lt;small&gt;&lt;br&gt;Yikes. That’s less than October, and I didn’t even break 100k this month. Black Friday took over, but no excuses—December needs to be better.&lt;/small&gt;&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Wake Up:&lt;/strong&gt; 03:31 &lt;small&gt;&lt;/small&gt;&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Avg Sleep:&lt;/strong&gt; 5:56h&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;Resting HR:&lt;/strong&gt; 43&lt;small&gt;&lt;/small&gt;&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;
      Still crushing the early wake-ups—didn’t miss a single day. Sleep and resting HR are improving, probably thanks to the cooler weather. And my Oura ring gave me a bunch of crowns this month (basically saying, &quot;Not bad, not bad...&quot;).
    &lt;/p&gt;
    &lt;h3&gt;&lt;a href=&quot;https://bold.video/&quot; target=&quot;_blank&quot;&gt;Bold&lt;/a&gt;&lt;/h3&gt;
      &lt;p&gt;This month was all about sharpening the product:&lt;/p&gt;
        &lt;ul class=&quot;stats&quot;&gt;
        &lt;li&gt;
            &lt;strong&gt;New Video Demo&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Made a new demo for outbound efforts. It’s simple, not polished to death, and I don’t hate it, which is a win. Might throw it on the landing page, too.
            &lt;br&gt;(Pro tip: outbound emails with YouTube links perform better—better link expectations, less spammy vibes. Plus, embedding it here gets a few views, which makes it look more legit 💡)&lt;/small&gt;
            &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/M_bdD6AC-a0?si=ATHRF6FrKB2m5-qP&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;CI Setup&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Not essential right now (still pre-PMF), but I added it anyway so I don’t forget to run tests. GitHub Actions for the win.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Search in Bold Portal&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Finally. This was overdue.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Drag-and-Drop Uploads&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Welcome to 2024. Took way too long, but hey, we’re here now.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;&lt;a href=&quot;https://showthem.com/&quot; target=&quot;_blank&quot;&gt;showthem.com&lt;/a&gt; updates&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;By the time you read this, Rob’s course is live—&lt;a href=&quot;https://showthem.com/&quot; target=&quot;_blank&quot;&gt;go check it out&lt;/a&gt;. I added Redis for detailed user progress tracking and made a bunch of tweaks. Smooth sailing overall, and I’m proud of what Rob accomplished with it.&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Finished the SaaS Academy Cohort&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Wrapped up the cohort. Lots of groundwork done. Now it’s time to execute and make Dan Martell proud.&lt;/small&gt;
        &lt;/li&gt;


        &lt;/ul&gt;
    &lt;h3&gt;&lt;a href=&quot;https://pidro.online/&quot; target=&quot;_blank&quot;&gt;Pidro&lt;/a&gt;&lt;/h3&gt;
    &lt;ul class=&quot;stats&quot;&gt;
        &lt;p&gt;Pidro ate most of my time this month, and while working in Unity is still slow, I’m glad I pushed through and got things done.&lt;/p&gt;
        &lt;li&gt;
            &lt;strong&gt;Sent 120k+ emails&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Sent 120k+ emails. Got only two angry replies (a record!) and landed ~100 new yearly subscriptions. Not bad. 😎&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Released a client update, 2.4.10&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Added &quot;Restore Purchases&quot; and a dynamic Paywall, so that we can run different prices via our CMS. Also, these were the first builds that I&#39;ve cranked out and published myself. Yay!&lt;/small&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;strong&gt;Pidro HQ Updates&lt;/strong&gt;&lt;br&gt;
            &lt;small&gt;Added more admin tools and automation to the backend. Everything is now faster and easier to manage.&lt;/small&gt;
        &lt;/li&gt;



    &lt;/ul&gt;
    &lt;p&gt;
        Things were, of course, bananas during Black Friday week at commercetools—but in a good way! Tons of fun tackling interesting challenges.
    &lt;/p&gt;
    &lt;p&gt;
      That’s it for November! During the monthly SaaS Academy momentum call, I set a goal for four Bold demos in December. Let’s see how it goes!
    &lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>The Story of Pidro - Part 1</title>
		<link href="https://marcelfahle.net/posts/pidro1/"/>
		<updated>2024-12-11T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/pidro1/</id>
		<content type="html">
&lt;p&gt;&lt;i&gt;This is a repost of a recent &lt;a href=&quot;https://pidro.online/&quot; target=&quot;_blank&quot;&gt;Pidro&lt;/a&gt; newsletter. It got some nice replies, and I thought it would be fun to share here. &lt;/i&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;p&gt;Hej Pidro friends,
&lt;br&gt;
&lt;br&gt;
Marcel here. I wanted to tell you a quick story about how Pidro came to be.&lt;/p&gt;

&lt;div class=&quot;img-wrapper&quot;&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2024-pidro1-1.jpg&quot; alt=&quot;Ko Lanta Island Life&quot;&gt;
        &lt;figcaption&gt;Ko Lanta Island Life&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;It starts in 2016.&lt;/p&gt;

&lt;p&gt;I was on a small island in Thailand called Ko Lanta. My then-girlfriend (now wife) Monika and I were spending a few months there, working remotely.&lt;/p&gt;

&lt;p&gt;Ko Lanta had this incredible coworking space called &lt;a href=&quot;https://kohub.org/&quot; target=&quot;_blank&quot;&gt;KoHub&lt;/a&gt;. Picture hammocks, palm trees, and a bunch of people typing away on laptops with coconuts by their side. That’s where I met Tommy.&lt;/p&gt;

&lt;p&gt;Tommy was a digital nomad from Finland, full of energy and ideas. One day, he asked me if I’d help him build a card game he and his friend Antti had been dreaming up.&lt;/p&gt;

&lt;p&gt;The game was called Pidro, and it was huge back in their hometown, a small place called Närpes, Finland. They wanted to bring it online so people could play wherever they were.&lt;/p&gt;

&lt;div class=&quot;img-wrapper&quot;&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2024-pidro1-2.png&quot; alt=&quot;The original Pidro Design Document&quot;&gt;
        &lt;figcaption&gt;The original Pidro Design Document&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;I thought it was a cool idea—but I was swamped with work. I told him, “Sorry, I can’t help.”&lt;/p&gt;

&lt;p&gt;Tommy and Antti didn’t give up. They found a service provider to build the game for them.&lt;/p&gt;

&lt;p&gt;By early 2017, they launched Pidro, and it was an instant hit. People loved it. So much so, the servers couldn’t handle the load.&lt;/p&gt;

&lt;p&gt;That’s when Tommy called me again.&lt;/p&gt;

&lt;p&gt;“Marcel,” he said, “the game’s crashing. Can you help us out?”&lt;/p&gt;

&lt;p&gt;Curious, I took a look at the server code. It was... a disaster.&lt;/p&gt;

&lt;p&gt;But here’s the thing: I’d been learning a new programming language that was perfect for fixing this exact kind of mess.&lt;/p&gt;

&lt;p&gt;So, I rolled up my sleeves, started optimizing the game, and before I knew it, I was part of the Pidro team.&lt;/p&gt;

&lt;p&gt;And that’s how it all started.&lt;/p&gt;

&lt;div class=&quot;img-wrapper&quot;&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2024-pidro1-3.png&quot; alt=&quot;The original Photoshop Design&quot;&gt;
        &lt;figcaption&gt;The original Photoshop Design&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Tomorrow, I’ll share the next part of the story—how Pidro grew into the game you know and love today.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>The Story of Pidro - Part 2</title>
		<link href="https://marcelfahle.net/posts/pidro2/"/>
		<updated>2024-12-12T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/pidro2/</id>
		<content type="html">
&lt;p&gt;&lt;i&gt;This is the second part of a recent &lt;a href=&quot;https://pidro.online/&quot; target=&quot;_blank&quot;&gt;Pidro&lt;/a&gt; newsletter. It got some nice replies, and I thought it would be fun to share here. &lt;/i&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;p&gt;Hello Pidro friends,
&lt;br&gt;
&lt;br&gt;
Marcel here again. Yesterday, I told you how Pidro started on a small island in Thailand with a simple idea: bring this beloved card game online so everyone could play.
&lt;/p&gt;

&lt;p&gt;
Today, I want to share what happened next—the good, the bad, and why we’re more fired up than ever to make Pidro the best it can be.
&lt;/p&gt;

&lt;p&gt;
  When Pidro first launched, it was a hit. Players loved it. But as more and more people joined, the cracks started to show.
&lt;/p&gt;

&lt;p&gt;
  The codebase we bought turned out to be... let’s just say, less than ideal. Fixing it felt like trying to replace parts of a car engine while it’s speeding down the highway—every tweak risked breaking something else.
&lt;/p&gt;

&lt;p&gt;
  As much as we loved Pidro, the financial results weren’t what we’d hoped. With families to support and bills to pay, we switched to “firefighting mode,” patching only the most critical issues and holding the game together with duct tape.
&lt;/p&gt;

&lt;p&gt;
  At one point, Jocke joined the team. He brought some fresh ideas, especially on the marketing side, but it wasn’t enough to break us out of the rut we’d fallen into. There were still too many issues, and the momentum we’d built early on was slipping away.
&lt;/p&gt;

&lt;p&gt;
  Eventually, Tommy stepped away from the project. He’s still a great friend, but today, the team is me, Antti, Jocke, and my wife Monika, who helps with game testing and support.
&lt;/p&gt;

&lt;p&gt;
  It was hard. Knowing we couldn’t give Pidro the time and care it deserved was heartbreaking.
&lt;/p&gt;

&lt;p&gt;
  Then, last spring, we hit a turning point.
&lt;/p&gt;

&lt;p&gt;
  As a team, we sat down and made a decision. We could let Pidro stay as it was, or we could dig in and rebuild it, step by step.
&lt;/p&gt;

&lt;p&gt;
  We chose to rebuild.
&lt;/p&gt;

&lt;p&gt;
  Since then, we’ve focused on steady progress. Not flashy updates, but constant improvements:
    &lt;br&gt;- Fixing the basics.
    &lt;br&gt;- Improving stability.
    &lt;br&gt;- Listening to you, the players.
&lt;/p&gt;

&lt;p&gt;
  It hasn’t been easy. It hasn’t been fast. But we’ve stuck with it.
&lt;/p&gt;

&lt;div class=&quot;img-wrapper&quot;&gt;
    &lt;figure&gt;
        &lt;img src=&quot;https://marcelfahle.net/img/blog/2024-pidro2-1.jpg&quot; alt=&quot;Antti, Jocke, Marcel, and Monika&quot; loading=&quot;lazy&quot;&gt;
        &lt;figcaption&gt;The current team&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;And here we are today: Antti, Jocke, Marcel, and Monika.&lt;/p&gt;

&lt;p&gt;We’re working hard to make Pidro the game it deserves to be. Better AI players, replacing dropped players, and introducing a Pedro play mode are all on the horizon.&lt;/p&gt;

&lt;p&gt;This amazing community makes it all worth it.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>New Year, New Me</title>
		<link href="https://marcelfahle.net/posts/2025/"/>
		<updated>2024-12-19T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/2025/</id>
		<content type="html">&lt;p&gt;A new calendar doesn&#39;t change you.&lt;/p&gt;
&lt;p&gt;Waiting for January 1st doesn&#39;t make it easier.&lt;/p&gt;
&lt;p&gt;January 1st doesn&#39;t give you more discipline.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t make you wake up earlier.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t fix your habits.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t give you motivation.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t erase the bad days.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t guarantee consistency.&lt;/p&gt;
&lt;p&gt;January 1st doesn&#39;t solve procrastination.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t do the work for you.&lt;/p&gt;
&lt;p&gt;January 1st is just another Monday. Or Wednesday. Or whatever.&lt;/p&gt;
&lt;p&gt;A Tuesday in December works just as well.&lt;/p&gt;
&lt;p&gt;A random Thursday in March is just as powerful.&lt;/p&gt;
&lt;p&gt;The day doesn&#39;t matter. What you do with it does.&lt;/p&gt;
&lt;p&gt;Start when you&#39;re ready. Or better: start before you&#39;re ready.&lt;/p&gt;
&lt;p&gt;January 1st isn&#39;t magic.&lt;/p&gt;
&lt;p&gt;You are.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Create Something</title>
		<link href="https://marcelfahle.net/posts/create/"/>
		<updated>2025-05-22T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/create/</id>
		<content type="html">&lt;p&gt;There are two sides.&lt;/p&gt;
&lt;p&gt;The people who create.&lt;br&gt;
And the people who consume.&lt;/p&gt;
&lt;p&gt;One side scrolls.&lt;br&gt;
One side ships.&lt;/p&gt;
&lt;p&gt;Watching isn’t creating.&lt;br&gt;
Scrolling isn’t building.&lt;br&gt;
Streaming isn’t shipping.&lt;br&gt;
Reading about it isn’t doing it.&lt;br&gt;
Reacting to other people’s work isn’t making your own.&lt;/p&gt;
&lt;p&gt;Liking. Commenting. Sharing.&lt;br&gt;
That’s not creating.&lt;/p&gt;
&lt;p&gt;Buying gear isn’t creating.&lt;br&gt;
Planning to create isn’t creating.&lt;br&gt;
Tweeting about creating isn’t creating.&lt;/p&gt;
&lt;p&gt;Coding is creating.&lt;br&gt;
Writing is creating.&lt;br&gt;
Sketching, filming, designing, making music, publishing.&lt;br&gt;
That’s creating.&lt;/p&gt;
&lt;p&gt;And every time you create, you step over the line.&lt;br&gt;
Onto the side where things are made.&lt;/p&gt;
&lt;p&gt;That side?&lt;br&gt;
It’s slower.&lt;br&gt;
It’s harder.&lt;br&gt;
It’s lonelier.&lt;/p&gt;
&lt;p&gt;But it’s better.&lt;/p&gt;
&lt;p&gt;Because in the end, you’ll forget most of what you watched.&lt;br&gt;
But you’ll remember what you made.&lt;/p&gt;
&lt;p&gt;And when you make something, even something small,&lt;br&gt;
you make progress.&lt;/p&gt;
&lt;p&gt;And progress is the part that feels like happiness.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>The Need to Explain</title>
		<link href="https://marcelfahle.net/posts/explain/"/>
		<updated>2025-07-13T00:00:00+00:00</updated>
		<id>https://marcelfahle.net/posts/explain/</id>
		<content type="html">&lt;p&gt;I went for a &lt;a href=&quot;https://www.strava.com/activities/15096619725&quot;&gt;run&lt;/a&gt; today. Not fast. Not impressive. Just a bit hard.&lt;/p&gt;
&lt;p&gt;And the first thing my brain did afterward (even during the run) was try to explain why it was slower than I&#39;m capable of.&lt;br&gt;
&amp;quot;I was listening to a podcast.&amp;quot;&lt;br&gt;
&amp;quot;Low HR run.&amp;quot;&lt;br&gt;
&amp;quot;Did a hard one yesterday, this was recovery.&amp;quot;&lt;/p&gt;
&lt;p&gt;As if the run needed a story. As if I had to make sure people knew it wasn&#39;t the whole picture.&lt;/p&gt;
&lt;p&gt;That reflex feels automatic. Social media trained it well. Everything needs a reason, a caption, a little performance. Bonus points if it looks intentional and admirable.&lt;/p&gt;
&lt;p&gt;But lately, I&#39;ve started pushing against that.&lt;br&gt;
I still post the runs. I like the stats. I like the progress.&lt;br&gt;
But I don&#39;t dress them up anymore.&lt;/p&gt;
&lt;p&gt;No captions. No clever lines. No golden-hour selfies.&lt;br&gt;
Just the run. Quietly logged. For me.&lt;/p&gt;
&lt;p&gt;And weirdly, that&#39;s hard. Because you don&#39;t get the dopamine. No fire emojis. No comments. No &amp;quot;you&#39;re crushing it.&amp;quot; Just silence.&lt;/p&gt;
&lt;p&gt;But I think that&#39;s the muscle I&#39;m trying to train.&lt;br&gt;
The one that doesn&#39;t need to impress.&lt;br&gt;
The one that can do the thing and not explain it.&lt;br&gt;
The one that&#39;s okay being unseen.&lt;/p&gt;
&lt;p&gt;I&#39;m not great at it yet. But I&#39;m paying attention.&lt;/p&gt;
</content>
	</entry>
	
	
	<entry>
		<title>Your AI Doesn&#39;t Know What &quot;Last Week&quot; Means</title>
		<link href="https://marcelfahle.net/posts/rag-temporal-range/"/>
		<updated>2026-03-03T11:54:04.504+00:00</updated>
		<id>https://marcelfahle.net/posts/rag-temporal-range/</id>
		<content type="html">&lt;p&gt;Ask a RAG system &amp;quot;how do I price my coaching program?&amp;quot; and it searches everything. Works great.&lt;/p&gt;&lt;p&gt;Now ask &amp;quot;what was discussed &lt;em&gt;last week&lt;/em&gt; about pricing?&amp;quot; and it does the exact same thing. Searches everything. Ignores the time window entirely.&lt;/p&gt;&lt;p&gt;We just fixed this in &lt;a href=&quot;https://boldvideo.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bold&lt;/a&gt;. I&amp;#x27;ve been unreasonably excited about it, partly because my first instinct was completely wrong.&lt;/p&gt;&lt;h2&gt;The trap I walked into&lt;/h2&gt;&lt;p&gt;Detect &amp;quot;last week&amp;quot; in the query, compute the dates, filter results. Done.&lt;/p&gt;&lt;p&gt;Except: our cache breaks immediately.&lt;/p&gt;&lt;p&gt;Bold caches query expansions so we don&amp;#x27;t burn money on duplicate LLM calls. But &amp;quot;last week&amp;quot; on a Friday points to different dates than &amp;quot;last week&amp;quot; on a Monday. A cached answer from Friday is stale before anyone&amp;#x27;s had their Monday coffee.&lt;/p&gt;&lt;p&gt;Ok, so add today&amp;#x27;s date to the cache key? Sure. But now you&amp;#x27;re blowing up the cache for &lt;em&gt;every&lt;/em&gt; question every day. Even stuff like &amp;quot;how do I find clients?&amp;quot; where the date couldn&amp;#x27;t matter less.&lt;/p&gt;&lt;h2&gt;The thing that clicked&lt;/h2&gt;&lt;p&gt;I kept staring at this for a while. Then it hit me.&lt;/p&gt;&lt;p&gt;What the user &lt;em&gt;means&lt;/em&gt; and what calendar dates that maps to are two completely different problems. &amp;quot;Last week&amp;quot; always means &amp;quot;last week.&amp;quot; That understanding doesn&amp;#x27;t expire. You can cache it forever. What specific dates &amp;quot;last week&amp;quot; points to on any given Tuesday? That&amp;#x27;s just math. Fast math.&lt;/p&gt;&lt;p&gt;So we split them. The LLM returns something like &lt;code&gt;{kind: &amp;quot;relative&amp;quot;, label: &amp;quot;last_week&amp;quot;}&lt;/code&gt;. We cache that. At search time, a tiny function turns it into real timestamps. No LLM call. No cache invalidation. Sub-millisecond.&lt;/p&gt;&lt;p&gt;Rich Hickey calls this decomplection. Pulling apart two things that got tangled together. The cache stores meaning. The runtime computes dates. They never touch.&lt;/p&gt;&lt;p&gt;It&amp;#x27;s one of those solutions that feels embarrassingly obvious once you see it.&lt;/p&gt;&lt;h2&gt;&amp;quot;Recently&amp;quot; is a different animal&lt;/h2&gt;&lt;p&gt;Someone asks &amp;quot;what&amp;#x27;s been discussed recently?&amp;quot; and the urge is to help. Pick a window. Thirty days? Ninety?&lt;/p&gt;&lt;p&gt;Don&amp;#x27;t.&lt;/p&gt;&lt;p&gt;&amp;quot;Recently&amp;quot; means something wildly different for a daily user versus someone who vanished for six months. There&amp;#x27;s no correct number. And if you guess wrong, you silently hide the exact content they were looking for. They get thin results, blame your product, and you never even know the filter existed.&lt;/p&gt;&lt;p&gt;So we don&amp;#x27;t filter. Vague input gets vague search. Let relevance do its job.&lt;/p&gt;&lt;h2&gt;Empty cupboard&lt;/h2&gt;&lt;p&gt;User asks about last week&amp;#x27;s pricing discussion. Nothing was published last week about pricing.&lt;/p&gt;&lt;p&gt;We drop the time filter. Search everything. One shot. The response says so: &amp;quot;Nothing from last week specifically, but here&amp;#x27;s what I found from the full library.&amp;quot;&lt;/p&gt;&lt;p&gt;We almost built graduated widening. Try 14 days, then 30, then everything. It looked smart on the whiteboard. In practice? Multiple search round-trips for a distinction nobody actually makes. People don&amp;#x27;t care whether the answer came from two weeks ago or two months ago. They just want the answer.&lt;/p&gt;&lt;h2&gt;Recaps want a story&lt;/h2&gt;&lt;p&gt;&amp;quot;What happened this month?&amp;quot; is not a search query. It&amp;#x27;s a request for narrative.&lt;/p&gt;&lt;p&gt;First this happened, then that happened, and it built on this other conversation from the week before. The connective tissue between sessions is the whole point of a recap.&lt;/p&gt;&lt;p&gt;So we detect recap intent and change the shape of the response. Order by date, not relevance. Frame it as a progression through time, not a ranked list of matches.&lt;/p&gt;&lt;p&gt;Bullets would kill it.&lt;/p&gt;&lt;h2&gt;One rule under everything&lt;/h2&gt;&lt;p&gt;Every decision here came from the same place: don&amp;#x27;t tangle things that are separate.&lt;/p&gt;&lt;p&gt;Meaning and dates. Filtering and ranking. Precise queries and vague ones. Stories and search results. Keep them apart and each piece stays simple, testable, easy to reason about. Mix them and you build something that looks clever on a whiteboard and falls apart the second real users touch it.&lt;/p&gt;&lt;p&gt;I&amp;#x27;m shipping this behind a flag. Watching the data. But the architecture feels right. And honestly, this is the most fun I&amp;#x27;ve had building a feature in months.&lt;/p&gt;</content>
	</entry>
	
	<entry>
		<title>The mind doesn&#39;t generate bad excuses. It generates good ones.</title>
		<link href="https://marcelfahle.net/posts/plausible/"/>
		<updated>2026-01-15T10:37:36.537+00:00</updated>
		<id>https://marcelfahle.net/posts/plausible/</id>
		<content type="html">&lt;p&gt;5 AM this morning, at work. Cold. Dark. I have a run scheduled at 6.&lt;/p&gt;&lt;p&gt;And right on cue, my brain served up a beauty: &amp;quot;You have that big deadline today. Skip the run. Focus on work.&amp;quot;&lt;/p&gt;&lt;p&gt;Notice how reasonable that sounds? That&amp;#x27;s the trap.&lt;/p&gt;&lt;p&gt;Your brain isn&amp;#x27;t stupid. That&amp;#x27;s the problem. It doesn&amp;#x27;t come up with obviously dumb excuses. It comes up with &lt;em&gt;good&lt;/em&gt; ones. Ones that sound responsible. &amp;quot;I should focus on work&amp;quot; hits different than &amp;quot;I don&amp;#x27;t wanna.&amp;quot;&lt;/p&gt;&lt;p&gt;Same outcome. Better packaging.&lt;/p&gt;&lt;p&gt;I caught myself mid-negotiation and thought: no. It&amp;#x27;s on the calendar. It&amp;#x27;s not a decision to be made. It&amp;#x27;s already been made. Past-me made it with a clear head. Present-me, standing in a warm house looking at darkness, is not someone I should be listening to.&lt;/p&gt;&lt;p&gt;The moment you start negotiating with yourself, you&amp;#x27;ve already lost.&lt;/p&gt;&lt;p&gt;I think this is the real work. Not discipline as forcing yourself through hard stuff. But discipline as &lt;em&gt;taking the decision off the table&lt;/em&gt;. The run isn&amp;#x27;t something I choose every Tuesday and Thursday. It&amp;#x27;s just what happens on Tuesdays and Thursdays.&lt;/p&gt;&lt;p&gt;Non-negotiable doesn&amp;#x27;t mean &amp;quot;really important.&amp;quot; It means: not up for discussion. Not even with yourself. Especially not with yourself.&lt;/p&gt;&lt;p&gt;I don&amp;#x27;t have this figured out. I still catch my own brain mid-excuse. But I&amp;#x27;m starting to notice: the better the excuse sounds, the more I should question it.&lt;/p&gt;&lt;p&gt;The shitty excuses aren&amp;#x27;t the danger. The good ones are.&lt;/p&gt;</content>
	</entry>
	
	<entry>
		<title>Your Job vs AI</title>
		<link href="https://marcelfahle.net/posts/ai-jobs/"/>
		<updated>2025-12-29T06:29:51.264+00:00</updated>
		<id>https://marcelfahle.net/posts/ai-jobs/</id>
		<content type="html">&lt;p&gt;I had this conversation like five times over the holidays. Family, friends, everyone&amp;#x27;s got opinions about AI taking jobs now.&lt;/p&gt;&lt;p&gt;And they all get it wrong the same way.&lt;/p&gt;&lt;p&gt;They think: AI does the task → fewer people needed → jobs disappear.&lt;/p&gt;&lt;p&gt;But that&amp;#x27;s not how efficiency has ever worked. Not once in history.&lt;/p&gt;&lt;p&gt;When design tools got cheap, we didn&amp;#x27;t hire fewer designers. We just started designing &lt;em&gt;everything&lt;/em&gt;. Pitch decks. Social posts. Landing pages. Internal docs. Stuff nobody would&amp;#x27;ve bothered with before Canva existed.&lt;/p&gt;&lt;p&gt;When video production dropped from tens of thousands to basically free, we didn&amp;#x27;t make fewer videos. We made 1000x more. Now every company thinks they need video for everything. Because they can.&lt;/p&gt;&lt;p&gt;This pattern is so consistent it has a name: the Jevons Paradox. Back in 1865, an economist noticed that when coal engines got more efficient, people didn&amp;#x27;t use less coal. They used way more. New use cases popped up everywhere.&lt;/p&gt;&lt;p&gt;It keeps happening. Computing went from something only the biggest corporations could afford to something in everyone&amp;#x27;s pocket. Software went from enterprise-only to every barbershop running the same tools as Fortune 500 companies.&lt;/p&gt;&lt;p&gt;And now it&amp;#x27;s coming for everything else.&lt;/p&gt;&lt;p&gt;AI is about to do the same thing to all the messy, human work. The stuff that was never automatable before. Reviewing contracts. Writing code. Research. Customer support. Campaigns.&lt;/p&gt;&lt;p&gt;The math changes when the cost of doing something drops to near zero. It&amp;#x27;s not about ROI anymore. It&amp;#x27;s about all the projects that never got started because they weren&amp;#x27;t worth the investment.&lt;/p&gt;&lt;p&gt;The 10-person company that never built custom software? Now someone ships a prototype in a weekend.&lt;/p&gt;&lt;p&gt;The startup that couldn&amp;#x27;t afford proper legal review? Now they can.&lt;/p&gt;&lt;p&gt;Jobs won&amp;#x27;t disappear. We&amp;#x27;ll just do way more work. Most of the AI tokens in the future will go toward things that nobody&amp;#x27;s doing today - because today, they&amp;#x27;re too expensive to even try.&lt;/p&gt;</content>
	</entry>
	
	<entry>
		<title>2025: 0 Goals Hit. Great Year.</title>
		<link href="https://marcelfahle.net/posts/2025-review/"/>
		<updated>2025-12-25T05:09:40.472+00:00</updated>
		<id>https://marcelfahle.net/posts/2025-review/</id>
		<content type="html">&lt;p&gt;Let&amp;#x27;s start with the big one.&lt;/p&gt;&lt;p&gt;In August, I got canned.&lt;/p&gt;&lt;p&gt;Eight years at the same gig (company got acquired halfway through). Principal-level work (without the title, because contractor). And then one day—done. Two weeks notice, handshake, goodbye.&lt;/p&gt;&lt;p&gt;I was shocked for about 15 seconds. And then I only saw opportunity.&lt;/p&gt;&lt;p&gt;I don&amp;#x27;t know why. That&amp;#x27;s just how my brain seems to work. Things go to shit, I get back up. Maybe I&amp;#x27;m delusional. But that&amp;#x27;s what happened.&lt;/p&gt;&lt;p&gt;Here&amp;#x27;s the thing though: I was way too comfortable in that job. It paid well, I had a good reputation, nothing left to prove. I was doing the work - Bold on the side, family stuff - but I wasn&amp;#x27;t really grinding.&lt;/p&gt;&lt;p&gt;Nothing wakes you up like having two kids, a wife, and suddenly no paycheck, though.&lt;/p&gt;&lt;p&gt;I went all-in on &lt;a href=&quot;https://boldvideo.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bold&lt;/a&gt;. No freelance safety net. No backup plan. Just a tiny bit of savings and a bet on myself.&lt;/p&gt;&lt;p&gt;Stressful? Yup, not gonna lie. Some weeks were rough. Still are.&lt;/p&gt;&lt;p&gt;Worth it? Ask me in a year.&lt;/p&gt;&lt;p&gt;Bold is starting to hit something. We found an angle in the market that people actually respond to. New customers are signing up because of it. No Lambo yet, but there&amp;#x27;s momentum. Real momentum.&lt;/p&gt;&lt;p&gt;Did I hit my financial goals for the year? Lol. Not even close. Hard to hit targets when you lose your income in August.&lt;/p&gt;&lt;p&gt;But I&amp;#x27;d rather miss the goal and find the path than hit the number and stay stuck. At least that&amp;#x27;s what I tell myself :)&lt;/p&gt;&lt;h3&gt;New Doors&lt;/h3&gt;&lt;p&gt;In March, I went to &lt;a href=&quot;https://microconf.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MicroConf&lt;/a&gt; in New Orleans. My second MicroConf. Amazing. The hallway track is unmatched. Came back fired up.&lt;/p&gt;&lt;div class=&quot;gallery grid grid-cols-2 md:grid-cols-3 gap-1 my-8&quot;&gt;
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/9799d38ad24b9d210a39c7a7ec248f5954d54f3e-4096x2731.jpg&quot; alt=&quot;MicroConf US 2025 - Sherry and Rob Walling&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;MicroConf US 2025 - Sherry and Rob Walling&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/b5feaa19f796aaf1838d1543d61c65a5eabf78ad-4096x2731.jpg&quot; alt=&quot;New Orleans&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;New Orleans&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/795d9c39aa764c71ee300a014225006efc22b22c-4096x2731.jpg&quot; alt=&quot;Alligators!&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Alligators!&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/456b328b02205af68849ee429f1a8ddc604f8d3c-4096x2731.jpg&quot; alt=&quot;Famous Bananas Foster at Brennan&#39;s&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Famous Bananas Foster at Brennan&#39;s&lt;/figcaption&gt;
          &lt;/figure&gt;
        &lt;/div&gt;&lt;p&gt;I also spent a year in &lt;a href=&quot;https://saasacademy.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SaaS Academy&lt;/a&gt;. Best investment I&amp;#x27;ve made.&lt;/p&gt;&lt;p&gt;It put everything in motion—the people I met, the coaches, the way it forced me to think bigger. I wouldn&amp;#x27;t be where I am without it.&lt;/p&gt;&lt;p&gt;My year ended right around when I got let go. Convenient timing, honestly.&lt;/p&gt;&lt;p&gt;Separately, I partnered up with &lt;a href=&quot;https://robhope.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Rob Hope&lt;/a&gt; this year. He&amp;#x27;s now my cofounder. If you know, you know. Having him on the team feels like a cheatcode (his first major contribution was acquiring &lt;a href=&quot;https://boldvideo.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;boldvideo.com&lt;/a&gt; 🥳).&lt;/p&gt;&lt;p&gt;I also teamed up with &lt;a href=&quot;https://founderwell.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vanessa at Founderwell&lt;/a&gt;—her program now runs on Bold. Very grateful for that one.&lt;/p&gt;&lt;p&gt;Between SaaS Academy and these new partnerships, I&amp;#x27;m now deep in the coaching and education space. Can&amp;#x27;t share everything yet, but it&amp;#x27;s exciting. We&amp;#x27;ll see where it goes.&lt;/p&gt;&lt;h3&gt;Running&lt;/h3&gt;&lt;p&gt;Let&amp;#x27;s start with me: 1,300km and 35,000 meters of elevation. Not bad, not great. A broken foot and a nasty flu didn&amp;#x27;t help. And once the job ended, running took a back seat to grinding. It&amp;#x27;s maintenance mode for now.&lt;/p&gt;&lt;p&gt;I&amp;#x27;ll get it back.&lt;/p&gt;&lt;p&gt;Monika though? This was her year.&lt;/p&gt;&lt;p&gt;As I&amp;#x27;m writing this, she&amp;#x27;s about to cross 3,000 kilometers. 80,000 meters of climbing. Insane.&lt;/p&gt;&lt;p&gt;We traveled to races with the kids. They watched their mom compete. Our oldest, Victoria, even joined her own running class—she and Monika share a coach now. That&amp;#x27;s pretty cool.&lt;/p&gt;&lt;p&gt;Some highlights:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Ultra Mediterranea&lt;/strong&gt; in Alcoi to kick off the year. Solid performance.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Confrides&lt;/strong&gt; – third overall, but we made rookie mistakes. She could&amp;#x27;ve won. That one stung but the learnings were super valuable.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;OCC at Chamonix&lt;/strong&gt; – always amazing. We&amp;#x27;re sort of like regulars there now. Our little ones did the kids races as well :)&lt;/p&gt;&lt;p&gt;&lt;strong&gt;World Championships in Canfranc&lt;/strong&gt; – the big one.&lt;/p&gt;&lt;p&gt;Canfranc was special. Monika ran for Team Lithuania. But it wasn&amp;#x27;t about the prestige. It was about breaking her pattern.&lt;/p&gt;&lt;p&gt;She always plays it safe. Never DNFs. Never blows up. But she also never really pushes the edge.&lt;/p&gt;&lt;p&gt;The time limits on that course were brutal, so we planned it down to the gram—nutrition, pacing, carbs, all of it. And she executed like clockwork.&lt;/p&gt;&lt;p&gt;That race changed how we think about what she&amp;#x27;s capable of.&lt;/p&gt;&lt;div class=&quot;gallery grid grid-cols-2 md:grid-cols-3 gap-1 my-8&quot;&gt;
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/054a947c1833e557562edfb1248651957ecf825e-4096x2731.jpg&quot; alt=&quot;Aiguille du Midi&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Aiguille du Midi&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/ca34bf97e6879161711fc75090eca3a320861dcf-4096x2731.jpg&quot; alt=&quot;Minis!&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Minis!&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/c43443c17187d3d023ba68894c1001dc00248334-4096x2731.jpg&quot; alt=&quot;Done!&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Done!&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/16919f7dd791595d9a62837b35099aa7f768bae3-4096x2731.jpg&quot; alt=&quot;Worldchamps, at KM 25 - Larraca&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Worldchamps, at KM 25 - Larraca&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/77787fe9ed3592d8017ae3cc2038972748a2c94a-4096x2731.jpg&quot; alt=&quot;Canfranc Worldchamps - descending down into the Astún ski resort&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Canfranc Worldchamps - descending down into the Astún ski resort&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/9f046710cd93737162109e8e4a1a7bb80df199b7-4096x2731.jpg&quot; alt=&quot;Done and Proud&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Done and Proud&lt;/figcaption&gt;
          &lt;/figure&gt;
        &lt;/div&gt;&lt;h3&gt;Other Adventures&lt;/h3&gt;&lt;p&gt;We spent the summer in Lithuania, as we always do. Countryside life, letting the kids run wild. This time, close friends from Spain came to visit—we were excited to show them around. It&amp;#x27;s always a great change of pace before the craziness picks up again.&lt;/p&gt;&lt;p&gt;From there we headed to Chamonix for a couple of weeks. Mountains, training, the OCC race. Hard to complain.&lt;/p&gt;&lt;p&gt;And then for Monika&amp;#x27;s 40th, we went to San Sebastián. One of our favorite places on earth. Good friends, great food. Doesn&amp;#x27;t get better than that.&lt;/p&gt;&lt;div class=&quot;gallery grid grid-cols-2 md:grid-cols-3 gap-1 my-8&quot;&gt;
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/e2615945253ca46bee57ee8ac25624528fbff67a-8155x5437.jpg&quot; alt=&quot;Vilnius alleys&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Vilnius alleys&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/822ff86299310f55b08d0830846aeef61ee48ad2-9445x6297.jpg&quot; alt=&quot;Gediminas&#39; Tower&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Gediminas&#39; Tower&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/d7df2c9ebe24a2703f444d5bb1e6f420a555db65-6336x9504.jpg&quot; alt=&quot;Užupis - My old neighborhood&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Užupis - My old neighborhood&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/88d1ce6b04ce8ab045780452d27e604f1bad921e-9504x6336.jpg&quot; alt=&quot;Špunka - My old watering hole&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Špunka - My old watering hole&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/7973c2b9f1cac85db2012f7a04dc5e08ebb3ffd8-4096x2731.jpg&quot; alt=&quot;Lac Blanc - 2352m&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Lac Blanc - 2352m&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/e33f6c202adbdc62bce36bed0b22e1b87be302d3-4096x2731.jpg&quot; alt=&quot;Chamonix from above&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Chamonix from above&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/40fbe1701356283e56346e31d0a3c68c2aa264cf-9504x6336.jpg&quot; alt=&quot;San Sebastián&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;San Sebastián&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/ebb9b4d1f68c1ef60408b8db01dcc13bc1065b92-9344x6229.jpg&quot; alt=&quot;Happy Birthday, my love&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Happy Birthday, my love&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/3f4c6957f9609a3d4a76874df84bd95918234cf5-9504x6336.jpg&quot; alt=&quot;Food in San Sebastián - there&#39;s no place like this one&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;Food in San Sebastián - there&#39;s no place like this one&lt;/figcaption&gt;
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/ba99b29a9ca1b6d0bb009fb634e76b48d8b3fc29-9185x6123.jpg&quot; alt=&quot;La Concha at night&quot; loading=&quot;lazy&quot;&gt;
            &lt;figcaption&gt;La Concha at night&lt;/figcaption&gt;
          &lt;/figure&gt;
        &lt;/div&gt;&lt;h3&gt;What I&amp;#x27;m Taking With Me&lt;/h3&gt;&lt;p&gt;I don&amp;#x27;t have some grand lesson figured out. But here&amp;#x27;s what I noticed: when shit gets uncomfortable, I work differently. Sharper. More focused. Less dicking around.&lt;/p&gt;&lt;p&gt;Losing the job forced me somewhere I&amp;#x27;ve never been mentally. And I&amp;#x27;m grateful for it.&lt;/p&gt;&lt;p&gt;I wake up at 3:30am almost every day. Maybe missed five or six mornings all year, usually travel days. Twenty minutes of breathwork, then straight to work. By the time the kids are done with school, I&amp;#x27;m done. Present. Not distracted. That part I&amp;#x27;m proud of.&lt;/p&gt;&lt;p&gt;I&amp;#x27;m learning to work faster, using AI to stay efficient. Still figuring out what works. I&amp;#x27;ll write more about that at some point.&lt;/p&gt;&lt;h3&gt;What&amp;#x27;s Next&lt;/h3&gt;&lt;p&gt;2025 was not the year I planned. It was better. Harder in some ways, sure. But more exciting.&lt;/p&gt;&lt;p&gt;We&amp;#x27;ve got stuff lined up for 2026. Monika&amp;#x27;s running Andorra (80K). We&amp;#x27;re heading back to Chamonix. Bold is getting traction.&lt;/p&gt;&lt;p&gt;I don&amp;#x27;t have it all figured out. But I&amp;#x27;m hungrier than I&amp;#x27;ve been in a while.&lt;/p&gt;&lt;p&gt;Let&amp;#x27;s see what happens.&lt;/p&gt;</content>
	</entry>
	
	<entry>
		<title>Letters from Ramona #1</title>
		<link href="https://marcelfahle.net/posts/letters-from-ramona-1/"/>
		<updated>2025-12-25T03:23:00+00:00</updated>
		<id>https://marcelfahle.net/posts/letters-from-ramona-1/</id>
		<content type="html">&lt;p&gt;&lt;em&gt;Ramona is a Chief People &amp;amp; Belonging Officer at a company that truly cares. She believes that culture isn&amp;#x27;t something you say—it&amp;#x27;s something you build, live, and evolve.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;When she&amp;#x27;s not pioneering new models of work where humans and AI thrive in sync, she writes letters. About difficult decisions. About the invisible labor of caring deeply. About what it really means to scale with humanity.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;This is her column.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;---&lt;/p&gt;&lt;p&gt;I&amp;#x27;m writing this from my home office in a city with 300 days of sunshine, since I&amp;#x27;ve been unable to sleep after the events of last week. I realize now that some of you may have misunderstood our intentions, so I&amp;#x27;d like to take this opportunity to clear up any lingering doubts.&lt;/p&gt;&lt;p&gt;First, I want to state that leadership cares deeply—&lt;em&gt;deeply&lt;/em&gt;—about every single one of you. In fact, the decision to deactivate your accounts that morning, just days before the holidays, was made entirely with your wellbeing in mind.&lt;/p&gt;&lt;p&gt;Some of the employees used to call me &amp;quot;La Madre&amp;quot; because of how much I cared. One engineer even got it tattooed, though he later said it was for his actual mother. Either way, I was honored.&lt;/p&gt;&lt;p&gt;You see, I believe culture isn&amp;#x27;t something you say. It&amp;#x27;s something you build, live, and evolve. And sometimes evolving means helping people find new opportunities to thrive—quickly, decisively, and without the burden of prolonged goodbyes.&lt;/p&gt;&lt;p&gt;Some have asked why we didn&amp;#x27;t provide advance notice. The answer is simple: clarity beats certainty, and courage beats process. Had we told you earlier, you might have spent your final weeks anxious and distracted. Instead, you got to enjoy a perfectly normal evening with your families, blissfully unaware. That&amp;#x27;s a gift. Feedback is also a gift, even when it stings—though in this case, we felt the feedback would sting less if delivered all at once, by email, with documents attached.&lt;/p&gt;&lt;p&gt;I also want to address the timing. Yes, it was the week before Christmas. But the holidays are about family, and what better present could we give you than more time with yours? Many of you had mentioned wanting better work-life balance. Consider this wish granted.&lt;/p&gt;&lt;p&gt;The severance package reflects our commitment to dignity over efficiency. Our lawyers worked very hard on those documents. You&amp;#x27;ll notice we even wished you well in the signature block.&lt;/p&gt;&lt;p&gt;For those asking about the all-hands meeting where leadership said &amp;quot;we&amp;#x27;re not just colleagues, we&amp;#x27;re family&amp;quot;—I want to clarify that this was taken out of context. Like family, sometimes we make difficult decisions for each other&amp;#x27;s own good. And like family, sometimes you don&amp;#x27;t find out until it&amp;#x27;s already done.&lt;/p&gt;&lt;p&gt;At our last all-hands, I led a guided meditation on &amp;quot;letting go.&amp;quot; In hindsight, this was perhaps too on the nose.&lt;/p&gt;&lt;p&gt;I&amp;#x27;ve heard some felt blindsided. But I want you to know: this was hard for me too. I found out about this decision just days before you did, which barely gave me time to finalize our &amp;quot;Happy Holidays, From Our Families to Yours&amp;quot; message from the People team.&lt;/p&gt;&lt;p&gt;Speaking of which—some have pointed out that I still have my job. This is true. Someone needs to stay behind to shepherd the remaining team through this period of renewal. That someone is me. I carry this burden with patience, courage, and real emotional stamina.&lt;/p&gt;&lt;p&gt;I personally shadow our engineers regularly. Not to monitor them—to understand their humanity. Just last month I spent an entire afternoon watching someone debug a search function. I learned so much about resilience that day. He cried. I cried. We both grew.&lt;/p&gt;&lt;p&gt;To my wonderful, caring, incredibly talented colleagues who remain: I see you. ✨&lt;/p&gt;&lt;p&gt;Each sparkle emoji is intentional.&lt;/p&gt;&lt;p&gt;And to those who&amp;#x27;ve transitioned: you&amp;#x27;re all doing well now, I assume.&lt;/p&gt;&lt;p&gt;With warmth, intentionality, and humanity,&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Ramona&lt;/strong&gt; Chief People &amp;amp; Belonging Officer &lt;em&gt;&amp;quot;Building cultures where talent thrives&amp;quot;&lt;/em&gt;&lt;/p&gt;</content>
	</entry>
	
	<entry>
		<title>Your “Basic” Is Someone Else’s Cheat Code</title>
		<link href="https://marcelfahle.net/posts/basic/"/>
		<updated>2025-11-22T05:34:59.105+00:00</updated>
		<id>https://marcelfahle.net/posts/basic/</id>
		<content type="html">&lt;p&gt;Most of us underestimate our own skills because we can’t remember how long it took to build them. After enough years, intuition takes over. You solve things fast, almost without thinking. And once something feels effortless, you assume it must be effortless for everyone else. But that is the lie our brain loves to tell.&lt;/p&gt;&lt;p&gt;Intuition hides the work you put in. It hides the reps, the late nights, the dead ends, and the stupid mistakes that taught you the important lessons. And when that happens, you start calling valuable skills “basic” and you stop sharing them because you think nobody needs them.&lt;/p&gt;&lt;p&gt;They do. What feels routine to you is a breakthrough for someone else. The things you dismiss are often the exact things others are desperate to learn. So whenever something feels “too obvious” to talk about, write it down anyway. That’s usually where your real expertise lives.&lt;/p&gt;</content>
	</entry>
	
	<entry>
		<title>San Sebastián</title>
		<link href="https://marcelfahle.net/posts/san-sebastian/"/>
		<updated>2025-11-17T04:49:05.671+00:00</updated>
		<id>https://marcelfahle.net/posts/san-sebastian/</id>
		<content type="html">&lt;p&gt;We spent a few days in San Sebastián last week with good friends, good food, and the kids buzzing around the pintxo bars. It felt good to be away from the screens for a bit and just enjoy the place. A short break, a little distance, and then back to real life with a clearer head.&lt;/p&gt;&lt;div class=&quot;gallery grid grid-cols-2 md:grid-cols-3 gap-1 my-8&quot;&gt;
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/9ca7e3f0dd0f9267d8c015049f18611a5af7cc1b-9254x6169.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/6ed92d97d38dc8d516add46e215c5c3355079913-9504x6336.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/6cff5b6083de09fcf1ec3d2c5962a277b747b779-8468x5645.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/3f4c6957f9609a3d4a76874df84bd95918234cf5-9504x6336.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/04eff0ed61913bd2ca60d1e4e38555038d04981c-6117x9175.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/ba99b29a9ca1b6d0bb009fb634e76b48d8b3fc29-9185x6123.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/a51a2e026f57f146bcbc635f7f34989a22456405-9504x6336.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/9cd70cb9c69164b5c4569ee0437cd3ea94d70fef-9504x6336.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/66db0e4e4c13e30814908a636aa94e34fd062293-9504x6336.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/0b21b4c6d7e0d27a03baa1eafe1d3617eea7f653-9392x6261.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/edb6ba5c49171620e9ac19acbb18f9e3ecc3c852-9504x6336.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        
          &lt;figure class=&quot;gallery-item&quot;&gt;
            &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/40fbe1701356283e56346e31d0a3c68c2aa264cf-9504x6336.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;
            
          &lt;/figure&gt;
        &lt;/div&gt;&lt;blockquote&gt;If I&amp;#x27;m an advocate for anything, it&amp;#x27;s to move. As far as you can, as much as you can. Across the ocean, or simply across the river. Walk in someone else&amp;#x27;s shoes or at least eat their food. It&amp;#x27;s a plus for everybody. &lt;/blockquote&gt;&lt;p&gt;~ A. B. ❤️&lt;/p&gt;</content>
	</entry>
	
	<entry>
		<title>30 Days of Breathing</title>
		<link href="https://marcelfahle.net/posts/breath/"/>
		<updated>2025-11-11T08:17:46.136+00:00</updated>
		<id>https://marcelfahle.net/posts/breath/</id>
		<content type="html">
        &lt;figure style=&quot;max-width: 320px; margin: 1.5rem auto&quot;&gt;
          &lt;img src=&quot;https://cdn.sanity.io/images/9mo6onv2/production/d20cc4d36af022b51425c2e38e1e43e61b392bf2-1170x2532.png&quot; alt=&quot;My Othership Streak&quot; width=&quot;1170&quot; height=&quot;2532&quot; loading=&quot;lazy&quot;&gt;
          &lt;figcaption style=&quot;font-size: 0.875rem; color: #6b7280; margin-top: 0.5rem;&quot;&gt;My Othership Streak&lt;/figcaption&gt;
        &lt;/figure&gt;
      &lt;p&gt;When I broke my foot last month, I needed something that still made me feel good, something I could actually do. So I started doing all kinds of exercises that didn’t involve the foot. Core, upper body, stretching.&lt;/p&gt;&lt;p&gt;And then I picked up this breathing app I’d had on my phone forever, &lt;a href=&quot;https://www.othership.us/app&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Othership&lt;/a&gt;. I’d tried it a few times before, always liked the idea, but never stuck with it.&lt;/p&gt;&lt;p&gt;This time I did.&lt;/p&gt;&lt;p&gt;Now it’s the first thing I do every morning at 3:40 a.m.&lt;br&gt;Fifteen minutes of breathing. No phone, no scrolling, just breath.&lt;/p&gt;&lt;p&gt;And a few times throughout the day too, when I hit that little afternoon fog, when focus fades, when I start clicking around like a zombie.&lt;/p&gt;&lt;p&gt;That part of my day is gone now.&lt;br&gt;I don’t need the coffee. I don’t need the YouTube break.&lt;/p&gt;&lt;p&gt;Just breathe, and I’m back.&lt;br&gt;More awake. Sharper. Clearer.&lt;/p&gt;&lt;p&gt;Feels a little like a drug, honestly.&lt;br&gt;A good one.&lt;/p&gt;&lt;p&gt;Can’t wait to see how regular breathwork affects my running performance once I’m back on the trails.&lt;/p&gt;</content>
	</entry>
	
	<entry>
		<title>Amp Free</title>
		<link href="https://marcelfahle.net/posts/amp-free/"/>
		<updated>2025-10-20T18:07:21.684+00:00</updated>
		<id>https://marcelfahle.net/posts/amp-free/</id>
		<content type="html">&lt;p&gt;&lt;a href=&quot;https://ampcode.com/free&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Amp Free&lt;/a&gt; dropped this week. I one-shotted a few things with it today and really like it. Sure, the context window is smaller and it seems to “think” a bit longer, but it’s a great setup.&lt;/p&gt;&lt;p&gt;It shows small ads and uses a mix of OSS and frontier models. You actually have to turn on &lt;em&gt;learning mode&lt;/em&gt; to help it improve, which feels refreshingly honest.&lt;/p&gt;&lt;p&gt;In a world where most of our token usage is subsidized by venture capital in pursuit of market share, I applaud what &lt;a href=&quot;https://sourcegraph.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Sourcegraph&lt;/a&gt; is doing. It’s nice to see someone building something sustainable instead of “free until the money runs out.”&lt;/p&gt;</content>
	</entry>
</feed>
