<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://snevsky.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://snevsky.com/" rel="alternate" type="text/html" /><updated>2026-07-05T22:53:08+00:00</updated><id>https://snevsky.com/feed.xml</id><title type="html">Serge Nevsky</title><subtitle>Agent-native engineering. Running fleets of AI agents that ship production software. Serge Nevsky, software engineer at Meta.</subtitle><author><name>Serge Nevsky</name></author><entry><title type="html">Building a Newborn Simulator</title><link href="https://snevsky.com/blog/newborn-simulator" rel="alternate" type="text/html" title="Building a Newborn Simulator" /><published>2021-06-16T00:00:00+00:00</published><updated>2021-06-16T00:00:00+00:00</updated><id>https://snevsky.com/blog/newborn-simulator</id><content type="html" xml:base="https://snevsky.com/blog/newborn-simulator"><![CDATA[<p>The mechanics of caring for a newborn child are surprising and fascinating.
It’s both challenging and simple at the same time.
And it’s not like any other experience in my life.</p>

<p>This inspired me to build a <a href="https://snevsky.com/newborn-simulator/">Newborn Simulator</a> over paternity leave.</p>

<aside>
<img src="/assets/img/newborn-simulator-01.png" alt="Screenshot from Newborn Simulator by Serge Nevsky" class="pure-img" />

</aside>

<p>I figured it would be a fun challenge to work on a project in the short bursts of free time I had between feedings, diapers and sleep.
The idea was to create a simple user interface consisting of buttons that give the “player” feedback about a simulated newborn’s condition.
The aim of the “game” is to keep the newborn satisfied, fed and clean.</p>

<h3 id="the-technology">The Technology</h3>

<p>For rapid interactive front end development <code class="language-plaintext highlighter-rouge">create-react-app</code> remains one of the best ways to get started.
I feel comfortable using React and this set of scripts served me well before.
Sometimes the best technology is the one you know best, particularly for short term projects such as this one.</p>

<p>It was my first time using the <code class="language-plaintext highlighter-rouge">gh-pages</code> package along with <code class="language-plaintext highlighter-rouge">create-react-app</code> but it made deploying the app an absolute breeze.
I also used <code class="language-plaintext highlighter-rouge">semantic-ui</code> for UI components and styling, which is a killer time saver for a developer.</p>

<h3 id="the-game">The Game</h3>

<p>The user interface is split into three columns. 
The first column displays the baby’s condition and metrics, as well as the player’s stamina.
The next column is a list of actions the player can take. 
And the final column displays game statistics.</p>

<p>The baby’s condition or “feeling” is equivalent to health points in classic gaming terms.
If feeling drops too low, the game is over.</p>

<p>Similarly the player’s stamina is almost like magic points, since taking actions “uses it up”.
It recovers when the player takes rest. 
If you neglect your stamina, you will pass out from exhaustion.</p>

<p>The baby’s weight grows as the player progresses through juggling the various actions.
The weight can also drop upon neglect. As such, weight is essentially the game score.</p>

<p>Finally the baby’s age is the in game clock. 
It doesn’t run out, but it does allow you to compare the rate of weight gain in different play throughs.
This is intended to motivate the player to replay the game again and do better.</p>

<p>Ultimately this is a game of balancing your responsibilities towards the child and your own health.
Not so different than raising a baby.</p>

<p><a href="https://snevsky.com/newborn-simulator/">Try playing the Newborn Simulator here.</a></p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[I built a browser game over paternity leave that simulates caring for a newborn: feed, change, rest, and manage your own stamina. Built with React.]]></summary></entry><entry><title type="html">Checking the Weather with Phillips Hue Light Bulbs</title><link href="https://snevsky.com/blog/weather-on-phillips-hue-light-bulb" rel="alternate" type="text/html" title="Checking the Weather with Phillips Hue Light Bulbs" /><published>2021-06-16T00:00:00+00:00</published><updated>2021-06-16T00:00:00+00:00</updated><id>https://snevsky.com/blog/weather-on-phillips-hue-light-bulb</id><content type="html" xml:base="https://snevsky.com/blog/weather-on-phillips-hue-light-bulb"><![CDATA[<p>Working from home during the pandemic created opportunities for short walks throughout the day. With a baby, this is all the more tempting. Unfortunately the weather in Kansas is neither cooperative nor predictable. This meant frequent lookups of current weather conditions.</p>

<p>Sometimes that means opening the Weather app on my iPhone. Sometimes it’s Google Assistant. And other times it’s opening the front door and peeking my head out.</p>

<p>But each of these actions carries friction. To eliminate that friction I built a service to set my Phillips Hue light bulb color based on the current weather conditions.</p>

<h3 id="table-of-contents">Table of Contents</h3>

<p><a href="#introduction">1. Introduction</a> <br />
<a href="#hue-api">2. Hue API</a> <br />
<a href="#open-weather-map-api">3. Open Weather Map API</a> <br />
<a href="#docker">4. Docker</a> <br />
<a href="#where-to-take-it-further">5. Where to take it further</a> <br />
<a href="#outcome">6. Outcome</a> <br /></p>

<h3 id="introduction">Introduction</h3>

<aside>
<img src="/assets/img/paris-painting-hue-light-bulb-1.jpg" alt="Painting of Paris lit by blue Phillips Hue light bulb" class="pure-img" />

</aside>

<p>An oil painting of Paris sits above our fireplace, lit by overhead spotlights. I replaced these spotlights with two color Phillips Hue light bulbs. The idea was to color the Parisian sky based on the weather conditions. The painting itself stays static, but the color of the bulbs gives an illusion of rain, snow, clear or cloudy skies.</p>

<p>For the implementation in Python, I used a combination of:</p>

<p>• The Phillips Hue API, via the <code class="language-plaintext highlighter-rouge">phue</code> library <br />
• The Open Weather Map API, via <code class="language-plaintext highlighter-rouge">pyowm</code></p>

<p>The inspiration for this combination of libraries came from <a href="https://github.com/friedmud/hue_weather">friedmud/hue_weather</a> which is the closest thing to what I was looking for. Similarly to the author, I found the IFTTT offerings extremely limited. Luckily these APIs are extremely approachable, and I had a working prototype after just two hours of coding.</p>

<h3 id="hue-api">Hue API</h3>

<p>The Hue API is extremely easy to use thanks to the <a href="https://github.com/studioimaginaire/phue">phue library</a>. You must authenticate your Hue Bridge to generate a credentials file. That means pushing the physical button on the device once, then immediately executing the <code class="language-plaintext highlighter-rouge">bridge.connect()</code> function. Once the credentials file exist, it need not be renewed. <a href="https://github.com/studioimaginaire/phue#examples">See details in their documentation.</a></p>

<p>Adjusting colors has many options, such as changing on/off, brightness, hue and various other properties individually. I found the easiest way to set colors was to do it manually in the iPhone app, then using the API to fetch the <code class="language-plaintext highlighter-rouge">xy</code> “coordinates” for that color. I noted those colors down, then set just the <code class="language-plaintext highlighter-rouge">xy</code> property on the light bulbs based on the weather condition.</p>

<h3 id="open-weather-map-api">Open Weather Map API</h3>

<p>For my use case, 15 mins between forecast checks (all day, so long as the light bulbs are turned on), is well within the boundaries of Open Weather Map’s free tier.</p>

<p>Accessing this API with the pyowm library was slightly confusing. My API key returned strange errors when using many of the library’s functions. It may be that some functions are limited by the pricing tier. Admittedly this was not clear in the documentation.</p>

<p>However I found that the <code class="language-plaintext highlighter-rouge">weather_manager.one_call()</code> function <em>did</em> work as expected and  met my use case. <a href="https://pyowm.readthedocs.io/en/latest/v3/code-recipes.html#onecall">See the documentation for “OneCall data” here</a>. I pass it the longitude and latitude for my location, and it returns a whole heck of a lot of information.</p>

<p>I was impressed with the API’s decision to use status codes to indicate weather conditions. I know very little about weather forecasting so I’m not sure if they are standard or not. They are conveniently similar to HTTP status codes. For instance 500 level status codes indicate various levels of rain, while 600 level status codes indicate snow. <a href="https://openweathermap.org/weather-conditions">See the documentation for those codes here.</a></p>

<p>Ultimately my implementation relies on the status codes entirely to display “rain” colors, “snow” colors, etc. I struggled with choosing distinct colors to indicate “cloudy”, “drizzle” or “clear”. Off white can’t mean everything. But I settled on some combinations that I could remember. For “clear” skies I also colored one of the bulbs warmer or cooler (more red or more blue) based on the current temperature.</p>

<h3 id="docker">Docker</h3>

<p>I’ve found that the easiest way for me to deploy services to my home server from my laptop is using Docker containers. It’s definitely possible to set up a daemon, but personally I’m more comfortable with Docker.</p>

<p>The Dockerfile installs <code class="language-plaintext highlighter-rouge">python3</code> and <code class="language-plaintext highlighter-rouge">pip</code> , then feeds the <code class="language-plaintext highlighter-rouge">requirements.txt</code> file to <code class="language-plaintext highlighter-rouge">pip</code> (which is the standard Python package manager). This is my preferred pattern for combining small Python services with Docker.</p>

<p>My home server runs this container continuously. There is a 15 minute wait between executions of the business logic within the code. It’s also quite fun to test, since I’m programming something in the physical world for once.</p>

<h3 id="where-to-take-it-further">Where to take it further</h3>

<p><span>1. </span> The code is currently far too specific to my house. It targets two light bulbs, intending to light a painting of Paris above a fireplace. That doesn’t have to be the case. Add a few more configuration options and this could be usable by others. At this point I’m happy if it inspires someone to try these APIs out themselves, using my code as a working example.</p>

<p><span>2. </span> Additionally I’m still not really that pleased with the color combinations. I struggled to come up with colors that indicated weather in an intuitive way. One possibility is color coding them more explicitly. There’s no reason the sky in Paris would ever turn green, so I did not use it. But if it can indicate something useful and distinct, such as a flash flood warning, then it makes sense.</p>

<p><span>3. </span> Finally, there are opportunities for similar projects. I have many other (non-weather indicating) Hue bulbs throughout the house. It would be convenient to change to warmer tones as sunset nears, similar to how f.lux works on the computer.</p>

<h3 id="outcome">Outcome</h3>

<p>It works! The service has been running for a few days now without issue. Since the weather has been clear for these last few days, I’ve only seen the bulb indicating heat turning increasingly red as the day progresses. Sometimes it stays red way after dark. I should raise the threshold for red, since it makes Paris look like an erupting volcano.</p>

<aside>
<img src="/assets/img/paris-painting-hue-light-bulb-2.jpg" alt="Painting of Paris lit by red Phillips Hue light bulb" class="pure-img" />

</aside>

<p>I’m satisfied with both of these APIs. It took very little time to set it up. As described earlier, this took about two hours total. The majority of that time was spent choosing colors to indicate the weather. That’s a good thing.</p>

<p>Given just how simple it was to spin this up, I’m tempted to jump further into home automation programming. It’s neat to program something that can be demoed to house guests.</p>

<p><a href="https://github.com/Dejital/hueweather">See the source code on Github here.</a></p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[I wired Phillips Hue bulbs to the OpenWeatherMap API so a painting of Paris shows the current weather: Python, Docker, and two hours of coding.]]></summary></entry><entry><title type="html">Newborn daughter</title><link href="https://snevsky.com/blog/maya" rel="alternate" type="text/html" title="Newborn daughter" /><published>2020-05-13T00:00:00+00:00</published><updated>2020-05-13T00:00:00+00:00</updated><id>https://snevsky.com/blog/maya</id><content type="html" xml:base="https://snevsky.com/blog/maya"><![CDATA[<p>Our daughter Maya was born just in time for lunch on April 25, 2020 at 11:55 AM. 👶</p>

<p>We are now happily enjoying her adorable facial expressions, her hilarious sounds that remind us of a squeaky door, and arguing over who she looks most like.</p>

<aside>
<img src="/assets/img/maya-01.jpg" alt="Newborn baby Maya Nevsky" class="pure-img" />

</aside>

<p>I know it would be impossible to convey the instant bonding that took place at birth to myself just weeks ago. The effect is astounding. All credits go to human nature.</p>

<p>Everyone is happy and healthy. Now we are working through learning this absolutely new mode of life. It can be exhausting, but it’s constant pleasure. And we remain thankful for all the moments.</p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[Our daughter Maya was born April 25, 2020. Notes on instant bonding and the first weeks of an absolutely new mode of life.]]></summary></entry><entry><title type="html">Authentication in ASP.NET Core Web API with Amazon Cognito</title><link href="https://snevsky.com/blog/dotnet-core-authentication-aws-cognito" rel="alternate" type="text/html" title="Authentication in ASP.NET Core Web API with Amazon Cognito" /><published>2018-09-18T00:00:00+00:00</published><updated>2018-09-18T00:00:00+00:00</updated><id>https://snevsky.com/blog/dotnet-core-authentication-aws-cognito</id><content type="html" xml:base="https://snevsky.com/blog/dotnet-core-authentication-aws-cognito"><![CDATA[<p><a href="https://aws.amazon.com/cognito/">Amazon Cognito</a> is the user management and authentication product in AWS. It allows for unified sign-up and sign-in flows across web and mobile apps.</p>

<p>I like it particularly for its pricing: Free for the first 50,000 monthly active users. The next 50,000 will cost you half a penny. Think you can sell your Director on that?</p>

<p>This article will show you how to set up Amazon Cognito in AWS, then configure Authentication for a Web API project to use Bearer tokens.</p>

<p>If you’d like to skip setting up Amazon Cognito in AWS, you can <a href="#add-authentication-to-web-api">skip straight to the C# portion</a> for code samples.</p>

<h3 id="table-of-contents">Table of Contents</h3>

<p><a href="#create-a-user-pool">1. Create a User Pool</a> <br />
<a href="#create-an-app-client">2. Create an App Client</a> <br />
<a href="#add-authentication-to-web-api">3. Add authentication to Web API</a> <br />
<a href="#register-users">4. Register Users</a> <br />
<a href="#authenticate-users">5. Authenticate Users</a> <br /></p>

<h3 id="create-a-user-pool">Create a User Pool</h3>

<p>Navigate to the Cognito home page from the AWS Management Console. You can search for Cognito in the AWS services search box, or click the link under the Services dropdown under “Security, Identity &amp; Compliance”.</p>

<p>From there you’ll see that Cognito is split into two parts: User Pools and Identity Pools. Identity Pools grant access to AWS services, but User Pools are what we want for API authentication.</p>

<p><img src="/assets/img/cognito-01.png" alt="Cognito Screenshot" /></p>

<p>Click on “Manage User Pools” and “Create a user pool”. Click either “Review defaults” or “Step through settings” to create the app pool. If you follow the defaults, you may want to adjust the aggressive default password policy.</p>

<p><img src="/assets/img/cognito-02.png" alt="Cognito Screenshot" /></p>

<h3 id="create-an-app-client">Create an App Client</h3>

<p>Each of your applications utilizing a Cognito User Pool is called an “app client”. You will need to register your Web API application with Cognito in this way.</p>

<p><img src="/assets/img/cognito-03.png" alt="Cognito Screenshot" /></p>

<p>Under “General settings” in the side bar, click on “App clients”. Then, “Add an app client”.</p>

<p><img src="/assets/img/cognito-04.png" alt="Cognito Screenshot" /></p>

<p>There are a few options here:</p>

<ul>
  <li>
    <p>Choose a custom name for your app client, keeping in mind any future application that utilizes the same app pool will use a separate app client.</p>
  </li>
  <li>
    <p>Check only “Enable sign-in API for server-based authentication”.</p>
  </li>
  <li>
    <p><strong>Do not check</strong> “Generate client secret”, “Only allow Custom Authentication” and “Enable username-password flow”.</p>
  </li>
</ul>

<p>Click “Create app client”.</p>

<p><img src="/assets/img/cognito-05.png" alt="Cognito Screenshot" /></p>

<p>An “App client id” will be generated for your app client. Take note of this ID as we will use it soon to configure the Web API.</p>

<h3 id="add-authentication-to-web-api">Add authentication to Web API</h3>

<p>ASP.NET Core Web API applications configure Authentication in the Startup
class. Navigate to the <code class="language-plaintext highlighter-rouge">Startup.cs</code> file in your solution Now find the
<code class="language-plaintext highlighter-rouge">ConfigureServices</code> function. Above the call to <code class="language-plaintext highlighter-rouge">AddMvc</code> include the <code class="language-plaintext highlighter-rouge">AddAuthentication</code> and <code class="language-plaintext highlighter-rouge">AddJwtBearer</code> extension methods:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">void</span> <span class="nf">ConfigureServices</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">services</span><span class="p">.</span><span class="nf">AddAuthentication</span><span class="p">(</span><span class="s">"Bearer"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">AddJwtBearer</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
        <span class="p">{</span>
            <span class="n">options</span><span class="p">.</span><span class="n">Audience</span> <span class="p">=</span> <span class="s">"Your App Client ID goes here!"</span><span class="p">;</span>
            <span class="n">options</span><span class="p">.</span><span class="n">Authority</span> <span class="p">=</span> <span class="s">"Your Authority URL goes here!"</span><span class="p">;</span>
        <span class="p">});</span>

    <span class="n">services</span><span class="p">.</span><span class="nf">AddMvc</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We are required to set just two options:</p>

<ul>
  <li>
    <p><strong>Audience</strong> represents the recipient of the token. In our case it is the <strong>App Client ID</strong>. To refresh your memory, it can be found in the AWS User Pools console under General Settings &gt; App clients. This is typically a random string of characters.</p>
  </li>
  <li>
    <p><strong>Authority</strong> is the address of the token issuing authentication server. This
will point to the user pool. For example, the authority for a user pool 
in the <code class="language-plaintext highlighter-rouge">us-east-1</code> region will be the following URL:</p>
  </li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://cognito-idp.us-east-1.amazonaws.com/&lt;userpoolID&gt;
</code></pre></div></div>

<p>Not sure what your region is? Check the URL in your AWS console. It’ll be the
same as the subdomain in the address bar like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://us-east-1.console.aws.amazon.com
</code></pre></div></div>

<p>Can’t remember your User Pool ID? In the User Pool console, it’s written across the top of <strong>General Settings</strong> as “Pool ID”.</p>

<p>Note: If you don’t care for the magic string <code class="language-plaintext highlighter-rouge">"Bearer"</code>, you can use <code class="language-plaintext highlighter-rouge">JwtBearerDefaults.AuthenticationScheme</code> from the <code class="language-plaintext highlighter-rouge">Microsoft.AspNetCore.Authentication.JwtBearer</code> namespace.</p>

<p>There is one other thing for the separate <code class="language-plaintext highlighter-rouge">Configure</code> function. You must add the Authentication middleware simply by including the following line:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IApplicationBuilder</span> <span class="n">app</span><span class="p">,</span> <span class="n">IHostingEnvironment</span> <span class="n">env</span><span class="p">)</span>
<span class="p">{</span>
    <span class="p">...</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseAuthentication</span><span class="p">();</span>
    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Authentication is now configured for the Web API application! You can secure any API controllers by using the <code class="language-plaintext highlighter-rouge">[Authorize]</code> attribute. For example:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">Authorize</span><span class="p">]</span>
<span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/values"</span><span class="p">)]</span>
<span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">ValuesController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">HttpGet</span><span class="p">]</span>
    <span class="k">public</span> <span class="n">ActionResult</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">Get</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="s">"Success!"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s test this out. Run the application and attempt to hit your Controller. I use <a href="https://www.getpostman.com/">Postman</a> for this.</p>

<p><img src="/assets/img/cognito-06.png" alt="Cognito Screenshot" /></p>

<p>From the above example <code class="language-plaintext highlighter-rouge">/api/values</code> returned the status <code class="language-plaintext highlighter-rouge">401 Unauthorized</code> which indicates we failed to authenticate. This is actually the behavior we want, since we did not provide a Bearer token!</p>

<p>Unfortunately we do not offer a way to retrieve Bearer tokens. Let’s mock out a sign-in flow in the next section so that we can verify our authentication code works.</p>

<h3 id="register-users">Register Users</h3>

<p>Create a new API Controller to deal with registering and authenticating users. For demonstration purposes this will be pretty simple.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">AuthenticationController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/register"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;&gt;</span> <span class="nf">Register</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="c1">// TODO: Use AWS SDK to register the user</span>
        <span class="k">return</span> <span class="nf">Ok</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/signin"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;&gt;</span> <span class="nf">SignIn</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="c1">// TODO: Pull in AWS SDK and generate our Bearer token</span>
        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="s">"The Bearer token!"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We will need to pull in the AWS SDK as Nuget packages. This will allow us to initiate requests to our Cognito User Pool from code.</p>

<p>Open Nuget Package Manager for the solution and add both packages <strong>AWSSDK.Core</strong> and <strong>AWSSDK.CognitoIdentityProvider</strong> to your project.</p>

<p><strong>Note!</strong> In order for the to work, the application needs two Environment Variables set: <code class="language-plaintext highlighter-rouge">AWS_SECRET_KEY</code> and <code class="language-plaintext highlighter-rouge">AWS_ACCESS_KEY_ID</code>.</p>

<p>If you haven’t generated these keys already: open up the AWS console home page, click your name in the top right corner, click “My Security Credentials”, expand the “Access keys” section and “Create New Access Key”. Set the Environment Variables for your project once they are generated.</p>

<p>Now we can fill out our <code class="language-plaintext highlighter-rouge">Register</code> method and start creating users.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">AuthenticationController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">_clientId</span> <span class="p">=</span> <span class="s">"Your App Client ID!"</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">RegionEndpoint</span> <span class="n">_region</span> <span class="p">=</span> <span class="n">RegionEndpoint</span><span class="p">.</span><span class="n">USWest2</span><span class="p">;</span>

    <span class="k">public</span> <span class="k">class</span> <span class="nc">User</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Username</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Password</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Email</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/register"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;&gt;</span> <span class="nf">Register</span><span class="p">(</span><span class="n">User</span> <span class="n">user</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">cognito</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AmazonCognitoIdentityProviderClient</span><span class="p">(</span><span class="n">_region</span><span class="p">);</span>

        <span class="kt">var</span> <span class="n">request</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SignUpRequest</span>
        <span class="p">{</span>
            <span class="n">ClientId</span> <span class="p">=</span> <span class="n">_clientId</span><span class="p">,</span>
            <span class="n">Password</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Password</span><span class="p">,</span>
            <span class="n">Username</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Username</span>
        <span class="p">};</span>

        <span class="kt">var</span> <span class="n">emailAttribute</span> <span class="p">=</span> <span class="k">new</span> <span class="n">AttributeType</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"email"</span><span class="p">,</span>
            <span class="n">Value</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Email</span>
        <span class="p">};</span>
        <span class="n">request</span><span class="p">.</span><span class="n">UserAttributes</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">emailAttribute</span><span class="p">);</span>

        <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">cognito</span><span class="p">.</span><span class="nf">SignUpAsync</span><span class="p">(</span><span class="n">request</span><span class="p">);</span>

        <span class="k">return</span> <span class="nf">Ok</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here are the few things going on in the above.</p>

<ul>
  <li>
    <p>The newly defined class User will act as our payload to the controller methods. This contains all the data we need to register the User, and it will be passed in with the POST body.</p>
  </li>
  <li>
    <p>First we initiliaze an instance of the <strong>Cognito Identity Provider Client</strong>. The constructor requires the region to be passed in, which I hard coded at the top of the controller. Ensure this region matches your User Pool!</p>
  </li>
  <li>
    <p>Next we create a SignUpRequest and fill in the values from the User passed in to the method. Note that the ClientId is assigned here as well, which is hard-coded at the top of the Controller in a private property. Ensure this matches your App Client ID!</p>
  </li>
  <li>
    <p>Additional registration attributes need to be added to the UserAttributes collection on the SignUpRequest object. Here an e-mail address attribute is added.</p>
  </li>
  <li>
    <p>Finally we <code class="language-plaintext highlighter-rouge">await</code> the <code class="language-plaintext highlighter-rouge">SignUpAsync</code> function to register the user. Return <code class="language-plaintext highlighter-rouge">Ok()</code> to indicate success.</p>
  </li>
</ul>

<p>Run the application and test it out with Postman.</p>

<p><img src="/assets/img/cognito-08.png" alt="Cognito Screenshot" /></p>

<p>Call the route <code class="language-plaintext highlighter-rouge">api/register</code> as a POST and pass a <code class="language-plaintext highlighter-rouge">User</code> JSON object in the raw body of the request:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
	</span><span class="nl">"Username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"test_user"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"Password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"test_password"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"Email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"test_user@snevsky.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Send the request and expect to get back a status of <code class="language-plaintext highlighter-rouge">200 OK</code>. If anything fails, such as password requirements, the exception details will be displayed as the response.</p>

<p>The first user has now been successfully registered with the Cognito User Pool!</p>

<p>However before this user can be used to authenticate, it needs to be confirmed. The easiest way to do this is now is manually through the AWS console:</p>

<p>Navigate to “Users and groups” under “General settings”. The newly registered user from above should be present on the list of users. It will be set as “Enabled” but “UNCOFIRMED”. Click the Username and confirm the user by clicking the “Confirm user” button. The user can now be used to authenticate.</p>

<h3 id="authenticate-users">Authenticate Users</h3>

<p>Back in the API controller, modify the <code class="language-plaintext highlighter-rouge">SignIn</code> controller method with the following:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">AuthenticationController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
    <span class="p">...</span>

    <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/signin"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;&gt;</span> <span class="nf">SignIn</span><span class="p">(</span><span class="n">User</span> <span class="n">user</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">cognito</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AmazonCognitoIdentityProviderClient</span><span class="p">(</span><span class="n">_region</span><span class="p">);</span>

        <span class="kt">var</span> <span class="n">request</span> <span class="p">=</span> <span class="k">new</span> <span class="n">AdminInitiateAuthRequest</span>
        <span class="p">{</span>
            <span class="n">UserPoolId</span> <span class="p">=</span> <span class="s">"Your User Pool ID!"</span><span class="p">,</span>
            <span class="n">ClientId</span> <span class="p">=</span> <span class="n">_clientId</span><span class="p">,</span>
            <span class="n">AuthFlow</span> <span class="p">=</span> <span class="n">AuthFlowType</span><span class="p">.</span><span class="n">ADMIN_NO_SRP_AUTH</span>
        <span class="p">};</span>

        <span class="n">request</span><span class="p">.</span><span class="n">AuthParameters</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="s">"USERNAME"</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">Username</span><span class="p">);</span>
        <span class="n">request</span><span class="p">.</span><span class="n">AuthParameters</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="s">"PASSWORD"</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">Password</span><span class="p">);</span>

        <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">cognito</span><span class="p">.</span><span class="nf">AdminInitiateAuthAsync</span><span class="p">(</span><span class="n">request</span><span class="p">);</span>

        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">AuthenticationResult</span><span class="p">.</span><span class="n">IdToken</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There are some new things happening here:</p>

<ul>
  <li>
    <p>We create an authentication request object that we will pass to the Cognito client. This requires the <strong>User Pool ID</strong>, the <strong>Client ID</strong> and <strong>authentication flow</strong> to be set.</p>
  </li>
  <li>
    <p>Then we add the authentication parameters: <code class="language-plaintext highlighter-rouge">USERNAME</code> and <code class="language-plaintext highlighter-rouge">PASSWORD</code>. For convenience this method accepts the same <code class="language-plaintext highlighter-rouge">User</code> object as before, so we use the data from that object to fill in these parameters.</p>
  </li>
  <li>
    <p>Finally we <code class="language-plaintext highlighter-rouge">await</code> initiating the authentication flow with Cognito. This returns a rich response object containing the Bearer token upon successful authentication. We can return the AccessToken on success.</p>
  </li>
</ul>

<p>Run the application to test this out with Postman.</p>

<p><img src="/assets/img/cognito-09.png" alt="Cognito Screenshot" /></p>

<p>Call the route <code class="language-plaintext highlighter-rouge">api/signin</code> as a POST and pass the <code class="language-plaintext highlighter-rouge">User</code> credentials as a JSON object in the raw body of the request.</p>

<p>Upon success the API returns a status of <code class="language-plaintext highlighter-rouge">200 OK</code> and the Bearer token. This value can now be used to authenticate the secured API controller: Recall from the <strong>Add authentication to Web API</strong> section, <code class="language-plaintext highlighter-rouge">ValuesController</code> was decorated with the <code class="language-plaintext highlighter-rouge">[Authorize]</code> attribute and returned a <code class="language-plaintext highlighter-rouge">401 Unauthorized</code> upon request. This token should allow the API consumer to resolve this error.</p>

<p><img src="/assets/img/cognito-11.png" alt="Cognito Screenshot" /></p>

<p>To pass the Bearer token to the API call, an Authorization header must be included with the request. The header Key is <code class="language-plaintext highlighter-rouge">Authorization</code> and the header Value follows the following format:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Bearer &lt;Bearer Token Value goes here!&gt;
</code></pre></div></div>

<p>Now send the request to <code class="language-plaintext highlighter-rouge">GET /api/values</code>.</p>

<p><img src="/assets/img/cognito-10.png" alt="Cognito Screenshot" /></p>

<p>The response “Hello” should be returned with a status of <code class="language-plaintext highlighter-rouge">200 OK</code> as expected.</p>

<p>This validates an entire successful flow of Authentication in ASP.NET Core Web API with Amazon Cognito!</p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[Step-by-step guide to Amazon Cognito authentication in an ASP.NET Core Web API: user pools, app clients, bearer tokens, and C# code samples.]]></summary></entry><entry><title type="html">Honeymoon in Maldives</title><link href="https://snevsky.com/blog/maldives" rel="alternate" type="text/html" title="Honeymoon in Maldives" /><published>2017-03-29T00:00:00+00:00</published><updated>2017-03-29T00:00:00+00:00</updated><id>https://snevsky.com/blog/maldives</id><content type="html" xml:base="https://snevsky.com/blog/maldives"><![CDATA[<p>This must to be the most beautiful place on the planet. When we arrived 
at Hadahaa Island in the Maldives for our honeymoon this year we 
immediately fell in love with it.</p>

<aside>
<img src="/assets/img/maldives-01.jpg" alt="" class="pure-img" />

</aside>
<aside>
<img src="/assets/img/maldives-02.jpg" alt="" class="pure-img" />

</aside>

<p>The tropical vegetation was a sparkling green. It always felt great 
to be outside even away from the ocean. The light breeze ruffled
the leaves just enough for a peaceful feeling.</p>

<aside>
<img src="/assets/img/maldives-03.jpg" alt="" class="pure-img" />

</aside>
<aside>
<img src="/assets/img/maldives-04.jpg" alt="" class="pure-img" />

</aside>

<p>Where the island really shined was the amazing ocean view. The shallow water
was a bright cyan tone, distinctly different from the deep blue water.</p>

<aside>
<img src="/assets/img/maldives-05.jpg" alt="" class="pure-img" />

</aside>

<p>There is no gradual fade from one color to another. The water suddenly 
creates a dark stripe deep into the horizon.</p>

<aside>
<img src="/assets/img/maldives-06.jpg" alt="" class="pure-img" />

</aside>
<aside>
<img src="/assets/img/maldives-07.jpg" alt="" class="pure-img" />

</aside>
<aside>
<img src="/assets/img/maldives-08.jpg" alt="" class="pure-img" />

</aside>

<p>The water was very clear. Mixed with my wife’s Mehendi designs still
covering her limbs after the wedding, it created some fantastic psychedelic
effects.</p>

<aside>
<img src="/assets/img/maldives-09.jpg" alt="" class="pure-img" />

</aside>

<p>The sand was spotless and practically white. From a distance it looked imagined.</p>

<aside>
<img src="/assets/img/maldives-10.jpg" alt="" class="pure-img" />

</aside>
<aside>
<img src="/assets/img/maldives-11.jpg" alt="" class="pure-img" />

</aside>

<p>With so few people around and nothing in the distance save for a few
passing ships, we felt like we were in a film, stuck on an uninhabited 
island.</p>

<aside>
<img src="/assets/img/maldives-12.jpg" alt="" class="pure-img" />

</aside>
<aside>
<img src="/assets/img/maldives-13.jpg" alt="" class="pure-img" />

</aside>
<aside>
<img src="/assets/img/maldives-14.jpg" alt="" class="pure-img" />

</aside>

<p>The resort was clean, well-kept and furnished nicely. This spa area became
our favorite hang-out spot on the island.</p>

<aside>
<img src="/assets/img/maldives-15.jpg" alt="" class="pure-img" />

</aside>

<p>Some moments felt like we were living in a computer desktop background.</p>

<aside>
<img src="/assets/img/maldives-16.jpg" alt="" class="pure-img" />

</aside>
<aside>
<img src="/assets/img/maldives-17.jpg" alt="" class="pure-img" />

</aside>

<p>With many nooks scattered strategically around the island, we
discovered several new places every day.</p>

<aside>
<img src="/assets/img/maldives-18.jpg" alt="" class="pure-img" />

</aside>
<aside>
<img src="/assets/img/maldives-19.jpg" alt="" class="pure-img" />

</aside>

<p>We promised ourselves we would come back to Maldives. We fell
for this small paradise.</p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[Photos and notes from our honeymoon on Hadahaa Island in the Maldives: cyan water, white sand, and a small paradise we promised to revisit.]]></summary></entry><entry><title type="html">Interviewing developers</title><link href="https://snevsky.com/blog/interviewing-developers" rel="alternate" type="text/html" title="Interviewing developers" /><published>2016-09-24T00:00:00+00:00</published><updated>2016-09-24T00:00:00+00:00</updated><id>https://snevsky.com/blog/interviewing-developers</id><content type="html" xml:base="https://snevsky.com/blog/interviewing-developers"><![CDATA[<p>I hear it a lot in Kansas City: <em>“We can’t find good engineering talent,”</em> followed 
by some excuse.</p>

<p><em>“Engineers are spoiled by the high salaries at big companies.”
“Programmers have so many options that it’s hard to compete.”
“Great developers are drawn to the “coasts” and not the Midwest.”</em></p>

<p>These premises are <strong>false</strong>.</p>

<p>The real reason developers aren’t taking offers from your company 
is the draining interview process.</p>

<p>In a market full of job opportunities, applicants don’t have the time 
to research your company thoroughly. We rely on the interview process 
to give us a sense of how working at the company will be like.</p>

<p>For this reason so many developers ignore potentially great 
fits because the company fails to put forth any sort of effort during 
the interview process.</p>

<h3 id="the-technical-phone-screen">The technical phone screen</h3>

<p>The biggest mistakes happen during the initial “phone screen.”</p>

<p>This phase typically consists of rapid fire questions pulled from 
the first result found on Google. <em>“Top 100 C# interview questions.”</em></p>

<p>Did you realize that the applicant read that same article last night 
in preparation?</p>

<p>Prefer home-grown questions. Prompt your developers to come up with 
a small handful of technical questions that apply directly to the work 
the new developer will be doing.</p>

<p>The nature of the questions is often problematic. An applicant’s ability to 
define an abstract class does not indicate their ability to 
actually implement it. Favor questions that display critical thinking 
rather than recall ability.</p>

<p>Consider a conversational approach. Ask which technologies 
interest the applicant and why. This answer <em>will</em> be genuine. You can 
get a great sense of their comfort level based on how they answer.</p>

<h3 id="give-them-a-reason-to-care">Give them a reason to care</h3>

<p>It’s true that the applicant applied to work for your company, 
but that does not mean they will take your written offer blindly.</p>

<p>Give them a reason to join the company. That initial phone screen is 
your <strong>only</strong> opportunity to make the applicant excited about continuing 
the process.</p>

<p>Explain the company’s vision, values, the work/life balance and 
the interesting problems you are solving. If any of 
these are missing, consider why you think you can hire a developer 
in the first place.</p>

<p>This will naturally bring out the applicant’s curiosity. Be 
prepared to answer their questions and hold a deep conversation.</p>

<p>If you have to prompt them, <em>“now do you have any questions for me?”</em> 
you’ve failed.</p>

<h3 id="represent-your-culture">Represent your culture</h3>

<p>The interview demonstrates your company’s culture. If an hour-long 
interrogation is representative, then prepare to bid farewell to the 
applicant.</p>

<p>Have developers from the team to meet the applicant. Include 
future coworkers in on the call or allow them to meet in the privacy 
of a conference room.</p>

<p>Allow the developer to solve an interesting problem either during the 
interview or at home. Don’t burden them. It should be light yet 
insightful, fun yet challenging. Leave them craving for more. The best 
developers will stay just for that.</p>

<p>It isn’t difficult to hire new team members if you display effort and 
interest.
Drop the culture of cross examination and aim to be inclusive.</p>

<p>The right talent will come to find <strong>you</strong>.</p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[Companies that cannot hire engineers usually have a draining interview process. How to fix the phone screen and make candidates want to join.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://snevsky.com/assets/img/applicant.png" /><media:content medium="image" url="https://snevsky.com/assets/img/applicant.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Moments between the minutes</title><link href="https://snevsky.com/blog/moments" rel="alternate" type="text/html" title="Moments between the minutes" /><published>2016-09-16T00:00:00+00:00</published><updated>2016-09-16T00:00:00+00:00</updated><id>https://snevsky.com/blog/moments</id><content type="html" xml:base="https://snevsky.com/blog/moments"><![CDATA[<p>I learned to play guitar in the moments between the minutes.</p>

<p>I did not look for a teacher. I did not sit down to deliberately practice. I did not keep my guitar in its case.</p>

<p>I kept the guitar by my side. I reached for it when my computer was rebooting, a file was downloading, a video was buffering, or code was building.</p>

<p>These moments added up to days, weeks and months. Years later I play with confidence and pride.</p>

<p>I didn’t need lessons like my guitar didn’t need its case. It was always ready.</p>

<p>Make it stupid easy to get started and you won’t be able to procrastinate. Forget the guitar case. Eliminate barriers to entry and keep tools ready at your side.</p>

<p>Become a pro without even trying. It just takes a few moments between the minutes.</p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[I learned guitar without lessons by keeping it out of its case and playing while code compiled. Remove the barriers and practice adds up on its own.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://snevsky.com/assets/img/guitar-neck.jpg" /><media:content medium="image" url="https://snevsky.com/assets/img/guitar-neck.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Kindle 2</title><link href="https://snevsky.com/blog/kindle" rel="alternate" type="text/html" title="Kindle 2" /><published>2016-09-15T00:00:00+00:00</published><updated>2016-09-15T00:00:00+00:00</updated><id>https://snevsky.com/blog/kindle</id><content type="html" xml:base="https://snevsky.com/blog/kindle"><![CDATA[<p>The Kindle 2 is the perfect product. I bought mine almost ten years ago. Today, that same exact unit goes with me everywhere I go. It isn’t annoying, it’s simple and it’s perfect.</p>

<p>In the time I’ve owned my Kindle, I’ve upgraded my smart phone, my laptop, my car, my career, even my house. But the Kindle remains the same.</p>

<p>Books pop up as expected every single time. The pages never require a software update to continue turning. The battery lasts for days without charging thanks to the lack of a high resolution screen and backlight. The software doesn’t change, so more powerful hardware isn’t necessary. The device is dead simple and just works.</p>

<p>When my iPhone’s text messaging app began lagging after updating to iOS 10, I began to realize. Devices like the Kindle 2 don’t exist anymore.</p>

<p>A decade from now eBook readers will be more powerful than my current work laptop. But the Kindle 2 will still work.</p>

<p>It serves one function and it does it perfectly.</p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[Ten years on, my Kindle 2 still works exactly like the day I bought it. On the rare joy of a device that serves one function perfectly.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://snevsky.com/assets/img/kindle.jpg" /><media:content medium="image" url="https://snevsky.com/assets/img/kindle.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The perfect notebook system for getting things done</title><link href="https://snevsky.com/blog/pocket-notebook" rel="alternate" type="text/html" title="The perfect notebook system for getting things done" /><published>2016-03-09T00:00:00+00:00</published><updated>2016-03-09T00:00:00+00:00</updated><id>https://snevsky.com/blog/pocket-notebook</id><content type="html" xml:base="https://snevsky.com/blog/pocket-notebook"><![CDATA[<p>I accidentally invented the single best system to keep a pocket notebook.
It turns out it’s really not so difficult at all. In fact, it turns 
out that a system isn’t even necessary.</p>

<p>Recently many <a href="http://bulletjournal.com/">journal systems</a> began popping 
up online. I know several people who picked one and tried it out.
Eventually they grew tired and moved on. It didn’t take very long 
either. <strong>Why didn’t it stick?</strong></p>

<p>These systems <em>look</em> beautiful. They aren’t difficult to understand. 
They’re easy to implement. They’re very, very tempting. But they don’t 
stick.</p>

<p>I’ve used a pocket notebook to keep track of tasks for a long time. 
I didn’t start by researching existing systems either. I simply started.</p>

<p><strong>The single best system to keep a notebook is to eliminate the system.</strong>
Define a loose set of rules, then let your mind wander.</p>

<p>My notebook looks like this.</p>

<p><img src="/assets/img/gtd-notebook-1.jpg" alt="Pocket GTD Notebook" class="pure-img" /></p>

<p>It’s small and it fits in my pocket. I can carry it around and never 
feel burdened.</p>

<p><img src="/assets/img/gtd-notebook-2.jpg" alt="Pocket GTD Notebook" class="pure-img" /></p>

<p>I tend to keep each day to two pages. I lay out my tasks on the left 
and do whatever else on the right.</p>

<p>I check off tasks when they’re complete. Sometimes I even prioritize tasks, as above.</p>

<p><img src="/assets/img/gtd-notebook-3.jpg" alt="Packing list" class="pure-img" /></p>

<p>Other times it’s barely legible. But it’ll make sense in that 
day. Packing lists are perfectly suited.</p>

<p><img src="/assets/img/gtd-notebook-4.jpg" alt="Workout notes" class="pure-img" /></p>

<p>I still haven’t found an app for my iPhone to track workouts with as 
much freedom as a notebook does.</p>

<p>Empty space is opportune for drawings…</p>

<p><img src="/assets/img/gtd-notebook-5.jpg" alt="Half-baked ideas" class="pure-img" /></p>

<p>…and half-baked concept album ideas.</p>

<p><strong>Do yourself a favor and pick up a pocket notebook.</strong> You can write and track anything 
you want without any cumbersome interface or system around it.</p>

<p>The perfect system is right there in your pocket, and it doesn’t need 
its battery charged.</p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[The best pocket notebook system is no system: a few loose rules, two pages a day, and freedom for tasks, packing lists, workouts, and sketches.]]></summary></entry><entry><title type="html">Blogging is backwards</title><link href="https://snevsky.com/blog/blogging" rel="alternate" type="text/html" title="Blogging is backwards" /><published>2016-02-19T00:00:00+00:00</published><updated>2016-02-19T00:00:00+00:00</updated><id>https://snevsky.com/blog/blogging</id><content type="html" xml:base="https://snevsky.com/blog/blogging"><![CDATA[<p>Though I’m constantly tempted, I just can’t seem to keep up a blog. I just find 
the format to be weird. Blogs are series of loosely related thoughts told to an 
ever-increasing audience on an irregular basis, <strong>backwards</strong>. It doesn’t make 
much sense to me. It never has.</p>

<p>It seems like the ideal blog visitor is one that familiarizes themselves with 
the entire blog from the start to finish. That is how we consume most 
information, isn’t it? I haven’t started a book from its last chapter since I 
was a toddler naively picking up a book upside down. So why are blog articles 
displayed by decreasing date of publication? It makes sense for news, 
since the most recent content is typically the most relevant. For the majority 
of blogs, I’m not sure that it does. Some blogs I frequent have terrific content 
from years back being pushed further out of sight by recent posts of 
questionable quality. Still, we accept that displaying our posts backwards is 
just the way it is.</p>

<p>I’m writing down some thoughts right now. Are they bound to be forgotten years 
later once this blog fills with hundreds of entries? Maybe it’ll sit at the 
top for a few months as I procrastinate writing a new article. Time will tell.</p>

<p><img src="/assets/img/geocities-new.png" alt="New New New" class="pure-img" /></p>

<p>So let me take you back. My first website was a fan page to my favorite Pokemon, 
Jigglypuff. I was around eight years old then. This was around 1998, before Y2K 
caused the planet to explode. I updated <em>Jigglypuff’s Puff Palace</em> fairly 
regularly with new artwork, animated gifs and webrings on one long scrolling 
page. Blogs weren’t a hit yet. Back then it was enough to add a small gif of the 
text “NEW” to highlight fresh content. But even that was just optional. 
I was satisfied with updating my gigantic index.html page, padded by copy and 
pasted javascript snippets, bringing it closer and closer to 
Platonic perfection. There was no instinct to place new content at the <em>top</em> of 
the page, in fact it was opposite.</p>

<p>But then blogs took off. Suddenly non-technical and technical users alike were 
putting content online with ease. Maybe LiveJournal is to blame for convincing 
those poor souls that content should be time driven, plotted on a calendar and 
date stamped under the header. Everyone started doing it. Wordpress made it 
better and easier. Older websites weren’t like that. I’d show you my old webring 
to prove it! – If I could.</p>

<p>And now I’m blogging about how I don’t like blogging. There’s a word for someone 
like that and it begins with a hippo. But the truth is I believe blogging really 
is an effective medium. Audiences are magically engaged when they notice the 
date stamp matching today. Somehow the age of content is evidence of its 
validity. You can see that in the comments for linked articles, calling five 
month old content “old”. That’s about as heartbreaking as dog years. The 
solution? <em>Write blog content frequently.</em> The audience will show up and they’ll 
love it, for that week.</p>

<p>Regardless of how weird the blog format might be, it works. Pretty brilliant 
guys like Seth Godin swear by it. I’ll ride the wave too, until the next big 
paradigm shift. May the backwards narrative be it, for now.</p>]]></content><author><name>Serge Nevsky</name></author><summary type="html"><![CDATA[Blogs publish backwards, newest first, and it never made sense to me. From a Geocities Jigglypuff fan page to why the format works anyway.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://snevsky.com/assets/img/opinionated-man.png" /><media:content medium="image" url="https://snevsky.com/assets/img/opinionated-man.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>