<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Iouri Khramtsov</title>
    <description>The latest articles on Forem by Iouri Khramtsov (@iourikhramtsov).</description>
    <link>https://forem.com/iourikhramtsov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F327754%2F897c8f21-35cc-4f81-bf3c-2e1d48a0e45c.jpg</url>
      <title>Forem: Iouri Khramtsov</title>
      <link>https://forem.com/iourikhramtsov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/iourikhramtsov"/>
    <language>en</language>
    <item>
      <title>Multi-threading without locks</title>
      <dc:creator>Iouri Khramtsov</dc:creator>
      <pubDate>Tue, 11 Feb 2020 00:42:19 +0000</pubDate>
      <link>https://forem.com/iourikhramtsov/multi-threading-without-locks-fgb</link>
      <guid>https://forem.com/iourikhramtsov/multi-threading-without-locks-fgb</guid>
      <description>&lt;p&gt;Just because you're sharing data structures between threads doesn't mean you have to use locks.&lt;/p&gt;

&lt;p&gt;Over the years I've come across a few approaches that help coordinate work between threads without having to make anyone wait. Here are a couple of them.&lt;/p&gt;

&lt;p&gt;WARNING: I stopped using these patterns because they are easy to misunderstand and misuse, which can lead to random hard-to-diagnose bugs. If you use these patterns, you're not helping the next develper understand the code better or avoid mistakes. You have been warned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference to an immutable object
&lt;/h2&gt;

&lt;p&gt;We know that in a multithreaded environment, modifying a shared object without locks is a bad idea. But what about replacing it?&lt;/p&gt;

&lt;p&gt;For example, consider this code. I'll use C#, but the same applies in Java, C++, or another traditional language with threads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FunWithThreads&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;InThreadOne&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="nf"&gt;InThreadTwo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;localThing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;SomeCalculation1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localThing&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;SomeCalculation2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localThing&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;finalResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;DoMoreWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localThing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Suppose lots of threads are concurrently calling &lt;code&gt;InThreadOne()&lt;/code&gt;, while lots of other threads are concurrently calling &lt;code&gt;InThreadTwo()&lt;/code&gt;. Will we ever get a wrong result, a race condition, or a conflict?&lt;/p&gt;

&lt;p&gt;Not really. &lt;code&gt;InThreadTwo()&lt;/code&gt; always refers to a consistent version of &lt;code&gt;this.thing&lt;/code&gt; because it places the reference to it into a local variable, and from then on uses only the local reference. If the calculations take too long, then maybe we get a result that's a bit stale, but that result will always be consistent. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it's useful&lt;/strong&gt;: I found this approach to be useful whenever &lt;code&gt;this.thing&lt;/code&gt; is a large, pre-computed data structure that gets infrequently updated but frequently read. Mostly I've used it to cache some data for internal capital markets applications to make the application feel snappier to the user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pitfalls&lt;/strong&gt;: It's really important to make sure that &lt;code&gt;this.thing&lt;/code&gt; is referenced only once at the beginning of the method, or else we're back to race conditions. It's also really important that &lt;code&gt;this.thing&lt;/code&gt; is never modified after it's assigned. That means lots of descriptive comments, very careful code reviews, and making sure that every developer that ever looks at this code knows all the gotchas. In a larger codebase, someone (maybe even you!) might still find a way to make a mistake somewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complex atomic operations
&lt;/h2&gt;

&lt;p&gt;The reason the above code works is because in C# (and in most other languages) assignment of references is an atomic operation, i.e. while it's executing, another thread can't sneak in mid-way and do something else.&lt;/p&gt;

&lt;p&gt;Modern processors have a few other interesting atomic operations, like &lt;a href="https://en.wikipedia.org/wiki/Compare-and-swap"&gt;compare and swap&lt;/a&gt;, implemented in C# as &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=netframework-4.8#System_Threading_Interlocked_CompareExchange_System_Int32__System_Int32_System_Int32_"&gt;&lt;code&gt;Interlocked.CompareExchange()&lt;/code&gt;&lt;/a&gt;, and in Java as &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html#compareAndSet-int-int-"&gt;&lt;code&gt;AtomicInteger.compareAndSet()&lt;/code&gt;&lt;/a&gt;. This function takes three arguments and does the following as an atomic operation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compares the first and the third arguments, then&lt;/li&gt;
&lt;li&gt;If they're equal, sets the first argument to be equal to the second argument, or&lt;/li&gt;
&lt;li&gt;If they're not equal, does nothing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One case where this can be used is when trying to limit requests for an expensive operation on a limited resource. For example, this code will drop any requests if the expensive operation is already running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Let's pretend this class is a singleton.&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExpensiveOperationLimiter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;isProcessing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;RequestExpensiveOperation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If isProcessing == 1, this does nothing and returns 1.&lt;/span&gt;
        &lt;span class="c1"&gt;// If isProcessing == 0, this sets isProcessing to 1 and returns 0. &lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wasProcessing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Interlocked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompareExchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;isProcessing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;wasProcessing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Only one invocation will be running at a time.&lt;/span&gt;
            &lt;span class="nf"&gt;DoExpensiveOperation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;isProcessing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoExpensiveOperation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you look at the above code carefully, you'll see that you can't replace &lt;code&gt;Interlocked.CompareExchange()&lt;/code&gt; and still have thread safety - there will always be an opportunity for another thread to sneak in between the instructions. &lt;/p&gt;

&lt;p&gt;With a bit of work, this code can also be expanded to queue up the next request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it's useful&lt;/strong&gt;: I've used this kind of code in situations where events can randomly trigger an application server's cache to refresh from a database or from another system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pitfalls&lt;/strong&gt;: Generally, I'd consider this to be a micro-optimization and not worth the effort. Many developers understand or have heard of locks, and have an easier time following them than any code that involves compare-and-set. As before, by using this, you're risking someone (including you!) eventually making a mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  The best way to avoid locks
&lt;/h2&gt;

&lt;p&gt;Don't write multithreaded code!&lt;/p&gt;

&lt;h2&gt;
  
  
  A shameless plug
&lt;/h2&gt;

&lt;p&gt;Check out &lt;a href="https://maesure.com/?r=dt3"&gt;Maesure&lt;/a&gt;, a time tracker that asks you every 10 minutes what you're doing.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>java</category>
      <category>cpp</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Show DEV.to: A time tracker that asks every 10 minutes what you're doing</title>
      <dc:creator>Iouri Khramtsov</dc:creator>
      <pubDate>Mon, 10 Feb 2020 23:05:30 +0000</pubDate>
      <link>https://forem.com/iourikhramtsov/a-time-tracker-that-asks-every-10-minutes-what-you-re-doing-3ji0</link>
      <guid>https://forem.com/iourikhramtsov/a-time-tracker-that-asks-every-10-minutes-what-you-re-doing-3ji0</guid>
      <description>&lt;h2&gt;
  
  
  What it is
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://maesure.com/?r=dt2"&gt;Maesure.com&lt;/a&gt; - can use it without signing up, though it's better to sign up.&lt;/p&gt;

&lt;p&gt;Here's what it looks like when you use it for a while:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zfc6dcxM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tba4ogq4tr620w182ii6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zfc6dcxM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tba4ogq4tr620w182ii6.png" alt="What Maesure looks like when you're using the browser."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But why?
&lt;/h2&gt;

&lt;p&gt;I tried a few ways to track time in the past using Excel spreadsheets and various apps. Usually I couldn't stand using it for more than a few days. Eventually I decided to try this approach — and surprisingly, it worked.&lt;/p&gt;

&lt;p&gt;It's less annoying that it seems at first glance. The first few times the interruptions are a bit jarring, but once you get used to it, you mostly don't notice it anymore. After a day or so, you will be able to answer most of Maesure's popups with one click or two keystrokes because it will accumulate a list of your most recent answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who uses it
&lt;/h2&gt;

&lt;p&gt;So far, it's been most popular among people who must track their time for work, either to bill clients or to report time on projects.&lt;/p&gt;

&lt;p&gt;A few, including me, have used it to improve personal productivity. I've written about my approach &lt;a href="https://i-kh.net/2019/10/20/how-i-keep-the-side-project-moving/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future plans
&lt;/h2&gt;

&lt;p&gt;Eventually there will be a paid version, though that's still some time away. In the meantime, feel free to let me know what's working or not working for you - I try to respond quickly to the users &lt;/p&gt;

&lt;p&gt;Cheers and happy Maesuring!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to make your codebase age well - an architect's perspective</title>
      <dc:creator>Iouri Khramtsov</dc:creator>
      <pubDate>Fri, 31 Jan 2020 13:15:36 +0000</pubDate>
      <link>https://forem.com/iourikhramtsov/how-to-make-your-project-age-well-an-architect-s-perspective-22gg</link>
      <guid>https://forem.com/iourikhramtsov/how-to-make-your-project-age-well-an-architect-s-perspective-22gg</guid>
      <description>&lt;p&gt;Why are some systems easy to update after a decade, while others are hard to understand after a year or two? And why are some companies drowning in bugs and need a month to complete a change that takes days in other companies?&lt;/p&gt;

&lt;p&gt;Some of the root causes may be organizational, but technical choices also make a big difference. &lt;/p&gt;

&lt;p&gt;After seeing plenty of projects on either end of the spectrum, here are the biggest differences between codebases that age well and the ones that don't.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Have less stuff to maintain
&lt;/h1&gt;

&lt;p&gt;The most maintainable systems I've seen had architects that pushed for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fewer features by being choosy about which features to implement&lt;/li&gt;
&lt;li&gt;Fewer technologies rather than more - e.g. avoiding polyglot microservices&lt;/li&gt;
&lt;li&gt;Sticking to the same patterns over time, even if they're imperfect (but good enough)&lt;/li&gt;
&lt;li&gt;Fewer services, technologies, 3rd party components, etc&lt;/li&gt;
&lt;li&gt;Fewer lines of code, relying more on third-party libraries and services&lt;/li&gt;
&lt;li&gt;Cleaning up old unused parts&lt;/li&gt;
&lt;li&gt;Fewer moving parts overall&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whatever is left would ideally be organized into groups of very similar-looking components so that whoever works on the system in five years would be able to understand it easily.&lt;/p&gt;

&lt;p&gt;The reverse of this — a technology/feature/sub-system sprawl — is one of the scariest ways in which the system can become unmaintainable because it is difficult to reverse. This can happen when generations of programmers come through an organization, decide that the old way of building things is bad, introduce new technologies/patterns/systems without replacing the old ones, and then are replaced by the next generation. Or by multiple teams working in parallel, independently picking their favorite tech stacks. To a lesser degree, this can happen when less experienced teams keep adding new tech to the mix without thinking through the complexity that comes out.&lt;/p&gt;

&lt;p&gt;Either way, the end result is a system that's hard to fully comprehend, easy to break, and probably has team members that can't or don't want to touch unfamiliar parts of the system. &lt;/p&gt;

&lt;h1&gt;
  
  
  2. Choose mature, popular technologies
&lt;/h1&gt;

&lt;p&gt;If you want your codebase to be easy to update in five years, don't use a technology that will be dead in five years. Or one that will change so much that upgrading becomes impractical. Anything built on Angular 1, Silverlight, or early milestone Spring Boot versions would be very uncomfortable to work with today. &lt;/p&gt;

&lt;p&gt;For this reason, I prefer to pick popular technologies that are already on v2 or v3 and have largely stabilized. &lt;/p&gt;

&lt;p&gt;As of early 2020, this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React: Good. Popular, widely used, about six years old. Probably the best pick on the front end.&lt;/li&gt;
&lt;li&gt;F#: Bad. Mature, but not widely used. Avoid unless you already have F# experts, or it's a solo side project.&lt;/li&gt;
&lt;li&gt;Svelte: Bad. Popular, high satisfaction, but new; might not survive or might change a lot in the next few years.&lt;/li&gt;
&lt;li&gt;C: Good. Will likely outlive all of us. Safe choice for its use cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a number of other, immediate advantages to picking popular, older technologies over newer or less popular ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More and better tools + libraries&lt;/li&gt;
&lt;li&gt;Lots of Stackoverflow answers to frequent problems&lt;/li&gt;
&lt;li&gt;Easy to find developers who can work with it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several variations on this theme; one of my coworkers calls himself "Service Pack 2 guy" (service pack 1 is too early for him). &lt;a href="https://mcfunley.com/choose-boring-technology"&gt;Choose boring technology&lt;/a&gt; is another popular essay that expands on this.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Tests, tests, tests
&lt;/h1&gt;

&lt;p&gt;At my new job, about a month into it, I needed to update a service written in Python that was rarely touched by others. It was written over the past few years, and the original authors have left the company some time ago. After spending some time browsing around the source code, I discovered that not only did it have great comments, but also 98% unit test coverage! In addition, it was part of a massive integration test suite. A couple weeks later when I was done, I was extremely confident that I didn't break any existing functionality, and that my new additions would probably work as expected.&lt;/p&gt;

&lt;p&gt;While automated tests can slow you down in the short run, on systems that have a long life they're totally worth the effort.&lt;/p&gt;

&lt;p&gt;I find that the tests that give the most return for the effort are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests, because they're easy to write and run, and &lt;/li&gt;
&lt;li&gt;End-to-end tests on the whole system, because they can uncover most integration issues and prove that the entire system works as expected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In some cases, the latter type can also be run in production after deployments to catch problems early.&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Help the next developer understand the system
&lt;/h1&gt;

&lt;p&gt;Whether it's a new developer joining a 100-person dev org, or a single dev looking at the source code that has not been touched in the past five years, they'll have a hard time modifying a system when they don't know what it does or why.&lt;/p&gt;

&lt;p&gt;So help them out in advance. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't surprise them with unexpected side-effects&lt;/li&gt;
&lt;li&gt;Leave comments that explain the "why"&lt;/li&gt;
&lt;li&gt;Have clear names&lt;/li&gt;
&lt;li&gt;High-level documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Who knows, the next developer could even be you!&lt;/p&gt;

&lt;h1&gt;
  
  
  A shameless plug
&lt;/h1&gt;

&lt;p&gt;Check out &lt;a href="https://maesure.com/?r=dt1"&gt;Maesure&lt;/a&gt;, a time tracker that asks you every 10 minutes what you're doing (the period is configurable). It seems to be most popular among those who have to track time for work, though a few are using it for their personal life.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>coding</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
