<?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>DEV Community: Alex Kotliarskyi</title>
    <description>The latest articles on DEV Community by Alex Kotliarskyi (@frantic).</description>
    <link>https://dev.to/frantic</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%2F148559%2F53913428-dc1e-4e7e-9989-1715b3f17d4b.jpg</url>
      <title>DEV Community: Alex Kotliarskyi</title>
      <link>https://dev.to/frantic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/frantic"/>
    <language>en</language>
    <item>
      <title>Moving my serverless project to Ruby on Rails</title>
      <dc:creator>Alex Kotliarskyi</dc:creator>
      <pubDate>Sat, 14 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/frantic/moving-my-serverless-project-to-ruby-on-rails-3j1i</link>
      <guid>https://dev.to/frantic/moving-my-serverless-project-to-ruby-on-rails-3j1i</guid>
      <description>&lt;p&gt;I have a small side project: &lt;a href="https://hacker.gifts"&gt;digital gift cards for hackers&lt;/a&gt;. It uses Shopify for all the store-related stuff: frontend, payments, refunds, reports, etc.&lt;/p&gt;

&lt;p&gt;But unlike regular digital products (ebooks, videos) I wanted each card that the user purchases from the store to be unique. So I made a script that generate personalized images and ran it manually for every order.&lt;/p&gt;

&lt;p&gt;The next logical step was automating this process. I started with serverless AWS Lambda. At the time it was a hot new tech and I wanted to learn more. It seemed very fitting for my use-case: single-responsibility functions that can run at any time and don’t require server maintenance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eNtrFlCZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://frantic.im/assets/back-to-rails/simple-lambda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eNtrFlCZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://frantic.im/assets/back-to-rails/simple-lambda.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was super easy to get started. I built a JavaScript function and deployed it to AWS Lambda, added Shopify web hook and it all worked!&lt;/p&gt;

&lt;p&gt;Early benefits of serverless (for hobby projects):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to get started&lt;/li&gt;
&lt;li&gt;Don’t have to configure or maintain servers&lt;/li&gt;
&lt;li&gt;Free for small loads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In reality, writing the simple Lambda functions turned out to be only 10% of the work.&lt;/p&gt;

&lt;p&gt;The time passed and my backend started getting more complex: I needed to store some state for each &lt;a href="https://hacker.gifts/products/space-invaders"&gt;puzzle&lt;/a&gt;, send confirmation emails, show order details page. What started as a simple function, grew into a bunch of serverless functions, SNS topics, S3 buckets, DynamoDB tables. All bound together with plenty of YAML glue, schema-less JSON objects passed around and random hardcoded configs in AWS console.&lt;/p&gt;

&lt;p&gt;I think it’s just a typical software development life-cycle: things grow organically, become a mess, and require some refactoring. Make it run first (discover market fit), then make it right (refactor to integrate the new discoveries).&lt;/p&gt;

&lt;p&gt;But this time it was different. I couldn’t refactor things as easily as I used to in the traditional monolithic apps. Here’s why:&lt;/p&gt;

&lt;p&gt;When the building blocks are too simple, the complexity moves into the interaction between the blocks.&lt;/p&gt;

&lt;p&gt;And the interactions between the serverless blocks happen &lt;em&gt;outside&lt;/em&gt; my application. A lambda publishes a message to SNS, another one picks it up and writes something to DynamoDB, the third one takes that new record and sends an email…&lt;/p&gt;

&lt;p&gt;I could test every single block in that flow, but I didn’t have the confidence in the overall process. What if publishing fails, how would I know that? How would system recover? Can I rollback and try again? Where do the logs go?&lt;/p&gt;

&lt;p&gt;Another swarm of problems was hiding in my configuration: bad Route 53 record, typos in SNS topics, wrong S3 bucket region. Tracing errors was a challenge, there’s no single log output I can look into.&lt;/p&gt;

&lt;p&gt;With serverless, I was no longer dealing with my project's domain, I was dealing with the distributed system's domain.&lt;/p&gt;

&lt;p&gt;At this point I felt fooled.&lt;/p&gt;

&lt;p&gt;I came for the easy way to deploy code and not think about servers, but in the end had to design my system around the platform’s limitations.&lt;/p&gt;

&lt;p&gt;Drawbacks of serverless (for hobby projects):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard to follow information flow&lt;/li&gt;
&lt;li&gt;Impossible to replicate production environment&lt;/li&gt;
&lt;li&gt;Slow iteration speed&lt;/li&gt;
&lt;li&gt;Lack of end-to-end testing&lt;/li&gt;
&lt;li&gt;Immature documentation (dominated by often outdated Medium posts)&lt;/li&gt;
&lt;li&gt;No conventions (have to make hundreds of unessential decisions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;—&lt;/p&gt;

&lt;p&gt;I was clearly not enjoying the serverless. So I decided to rewrite it. After all, it is a side project I’m doing for fun. The tech stack of choice — Ruby on Rails.&lt;/p&gt;

&lt;p&gt;I haven’t used Rails since 2013, and for the last 8 years at Facebook I’ve been mostly doing JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BVMd2YTO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://frantic.im/assets/back-to-rails/logo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BVMd2YTO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://frantic.im/assets/back-to-rails/logo.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The experience of picking Rails back up was really nice but… uneventful. Nothing really changed that much. Few things got added, a few small things moved around.&lt;/p&gt;

&lt;p&gt;Of course I did &lt;a href="https://github.com/rails/rails/issues/38060"&gt;hit some magical Ruby issues&lt;/a&gt;. But unlike my typical experience with JavaScript, I was quickly able to find the solution.&lt;/p&gt;

&lt;p&gt;Rails comes with so many things built-in and configured. Over the years without Rails I used to gluing random JavaScript libraries together to roll my own routing, file storage wrappers, email preview pipeline, managing secrets, test setup with fixtures, database migrations, logging, performance reporting, deployment scripts. With Rails I didn’t have to think about all these details and could simply focus on making product-visible changes.&lt;/p&gt;

&lt;p&gt;It was as like driving a Tesla after years of making my own scrappy cars. Similar components, but all configured and aligned to work well together.&lt;/p&gt;

&lt;p&gt;Benefits of Rails (for hobby projects):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conventions&lt;/li&gt;
&lt;li&gt;Tooling, libraries&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Monolith is easy to understand and test&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drawbacks of Rails (for hobby projects):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feels heavyweight in the beginning&lt;/li&gt;
&lt;li&gt;Hurts if your opinions differ from Rails conventions&lt;/li&gt;
&lt;li&gt;Have to host on a server&lt;/li&gt;
&lt;li&gt;Doesn’t sound cool in 2020 (anymore and maybe yet)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;—&lt;/p&gt;

&lt;p&gt;Serverless is like a black hole. It promised exciting adventures, but the gravity sucked me in and I spend most of my efforts dealing with its complexity, instead of focusing on my product.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>serverless</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Things that are not strings</title>
      <dc:creator>Alex Kotliarskyi</dc:creator>
      <pubDate>Wed, 19 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/frantic/things-that-are-not-strings-2c14</link>
      <guid>https://dev.to/frantic/things-that-are-not-strings-2c14</guid>
      <description>&lt;p&gt;As programmers, we have a collective delusion that anything that can be represented as a string, is a string. This thinking causes a whole bunch of problems.&lt;/p&gt;

&lt;p&gt;Let’s take SQL for example. Every API in every programming language that I’ve seen considers SQL statement a string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function execute(sql: string): Promise&amp;lt;Result&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with this API is that not every string is a valid SQL (nor sometimes it is the SQL you actually want to run).&lt;/p&gt;

&lt;p&gt;Here’s a classic example of the misuse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const query = 'SELECT * FROM posts WHERE id = '
  + params.id;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example &lt;code&gt;params.id&lt;/code&gt; can be anything, including invalid or malicious SQL.&lt;/p&gt;

&lt;p&gt;The root problem here is not the lack of sanitization. The problem is that SQL is treated as a string.&lt;/p&gt;

&lt;p&gt;Think about JSON for another example. You could certainly implement adding an item to a hash by doing this (I hope this code makes you cringe):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function addKeyValue(json, key, value) {
  return json.substr(0, json.length - 1)
    + ', "' + key + '": "' + value + '"}';
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As with the SQL example, you could add escaping and sanitization, but it’s just hacks hiding the real problem:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A string can be a representation of a thing, but it’s not the thing itself.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And it’s not only about concatenating strings. Can you spot the problem with this function? &lt;small title="Is this safe? https://evil.com/https://safe.com/"&gt;(hover to see the answer)&lt;/small&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function isSafeDomain(url: string): boolean {
  return url.includes('https://safe.com/');
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or in this one? &lt;small title="This code could be prone to a timing attack"&gt;(hover to see the answer)&lt;/small&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function checkPassword(pass: string, hash: string): boolean {
  return bcrypt(pass) === hash;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Strings are lower level, and thus are much more flexible than they need to be to properly implement valid operations.&lt;/p&gt;

&lt;p&gt;Incomplete list of things that are not strings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL&lt;/li&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;URL&lt;/li&gt;
&lt;li&gt;File path&lt;/li&gt;
&lt;li&gt;Password&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Things are… things
&lt;/h2&gt;

&lt;p&gt;You can save yourself a lot of headaches if you stop treating everything that can be represented as a string, as a string.&lt;/p&gt;

&lt;p&gt;Both OO and FP styles allow for abstracting away something as a type or a class. You can make a closed opaque structure for the thing and limit the ways it can be constructed.&lt;/p&gt;

&lt;p&gt;For example, for SQL, you might want to make sure it’s only created from static string literals.&lt;/p&gt;

&lt;p&gt;Of course, at some point, you will need to serialize the thing into a string to pass it further, and that fine, as long as you never carelessly take a string and unserialize it yourself.&lt;/p&gt;

&lt;p&gt;Here’s a few libraries for inspiration of how to treat things as… things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL: &lt;a href="https://github.com/gajus/slonik"&gt;Slonik&lt;/a&gt;, &lt;a href="http://knexjs.org/"&gt;Knex&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;HTML: React, Elm, &lt;a href="https://github.com/tonsky/rum"&gt;rum&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JSON: a dictionary in any programming language&lt;/li&gt;
&lt;li&gt;URL: &lt;a href="https://docs.rs/url/2.1.1/url/"&gt;url - Rust&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;File paths: &lt;a href="https://doc.rust-lang.org/std/path/struct.Path.html"&gt;std::path::Path - Rust&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>string</category>
    </item>
    <item>
      <title>How not to use Redux: SET actions</title>
      <dc:creator>Alex Kotliarskyi</dc:creator>
      <pubDate>Sun, 15 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/frantic/how-not-to-use-redux-set-actions-25pg</link>
      <guid>https://dev.to/frantic/how-not-to-use-redux-set-actions-25pg</guid>
      <description>&lt;p&gt;&lt;em&gt;TL;DR: Instead of thinking about your actions as something that changes application state, consider them events instead.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article by Flux I mean one way data flow pattern implementations like Redux, useReducer or Elm Architecture&lt;/p&gt;

&lt;p&gt;Every time I see an action with a name that starts with &lt;code&gt;SET_*&lt;/code&gt;, I know there’s a problem.&lt;/p&gt;

&lt;p&gt;Consider this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Counter(props) {
  function onClick() {
    props.dispatch({
      type: "SET_VALUE",
      value: props.value + 1
    });
  }
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div&amp;gt;Value: {props.value}&amp;lt;/div&amp;gt;
      &amp;lt;button onClick={onClick}&amp;gt;+&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function reducer(state, action) {
  if (action.type === "SET_VALUE") {
    return { value: action.value };
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problem #1: Business logic inside the component
&lt;/h2&gt;

&lt;p&gt;In this example the bussness logic is incrementing the value, i.e. &lt;code&gt;value = value + 1&lt;/code&gt;. If we use &lt;code&gt;SET_*&lt;/code&gt; action, the logic ends up inside the component. It’s not great because the React component should not care about these details, its main purpose is rendering and dispatching actions.&lt;/p&gt;

&lt;p&gt;As the application and the team grows, it’s going to be very tempting to put more stuff into the component. E.g. if we wanted to have a max value for the counter, the most intuitive thing in this example would be to put it into the &lt;code&gt;onClick&lt;/code&gt; handler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem #2: Not leaveraging reducer
&lt;/h2&gt;

&lt;p&gt;Reducer is a beautiful pattern that’s older than JavaScript itself. Given a state and an action reducer returns a new state. It’s easy to type check, easy to test, easy to reason about.&lt;/p&gt;

&lt;p&gt;When we put the business logic outside of reducer, we miss out on all these advantages. The &lt;code&gt;SET_*&lt;/code&gt; actions essentially turn the predictable store into a global variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem #3: Hard to debug and test
&lt;/h2&gt;

&lt;p&gt;Imagine our counter component isn’t behaving right: after pressing the &lt;code&gt;+&lt;/code&gt; button the value is not what we expect. Let’s check Redux devtools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old State: {value: 3}
Action: {type: "SET_VALUE", value: 42}
New State: {value: 42}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is correct, but not helpful. How did we compute &lt;code&gt;42&lt;/code&gt; as the new value to be set? 🤷‍♂️&lt;/p&gt;

&lt;p&gt;Writing tests for &lt;code&gt;SET_*&lt;/code&gt; actions is also not great. The tests end up very dumb and the aren’t really testing anything useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test("SET_VALUE action", () =&amp;gt; {
  store.dispatch({ type: "SET_VALUE", value: 3 });
  expect(store.getState().value).toEqual(3);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test the business logic that lives inside React component we’d have to spend a lot of effort setting up mock renderer, simulating click event, etc.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Better Way
&lt;/h1&gt;

&lt;p&gt;Let’s think about our actions as objects describing user’s intent. In the simple &lt;code&gt;Counter&lt;/code&gt; component example from above, when the user clicks &lt;code&gt;+&lt;/code&gt; the user wants the app to &lt;em&gt;increment&lt;/em&gt; the counter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Counter(props) {
  function onClick() {
    props.dispatch({ type: "INCREMENT" });
  }
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div&amp;gt;Value: {props.value}&amp;lt;/div&amp;gt;
      &amp;lt;button onClick={onClick}&amp;gt;+&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function reducer(state, action) {
  if (action.type === "INCREMENT") {
    return { value: state.value + 1 };
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the React component in this case doesn’t care how this event is handled, it just tells the system about what happened.&lt;/p&gt;

&lt;p&gt;The reducer tests in this case are more meaningful, capturing the essense of the business logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test("INCREMENT action", () =&amp;gt; {
  const store = createStore({ value: 2 });
  store.dispatch({ type: "INCREMENT" });
  expect(store.getState().value).toEqual(3);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Actions are events
&lt;/h2&gt;

&lt;p&gt;The secret here is in the mindset. Instead of thinking about your actions as something that changes application state, consider them events instead. These events could represent user intent (clicking a button, typing, etc.) or a notification from external system (timers, HTTP response, etc.)&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SET_LOGGED_IN&lt;/code&gt; → &lt;code&gt;GOT_ACCESS_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SET_TODOS_LIST&lt;/code&gt; → &lt;code&gt;LOADED_TODOS_FROM_SERVER&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;P.S. The “no &lt;code&gt;SET_*&lt;/code&gt; actions” rule can be generalized: actions should not be derrived from state. If you need some information from state to construct an action, it’s a sign that the action should be simpler and the computation you are trying to perform should probably live in the reducer.&lt;/p&gt;

</description>
      <category>react</category>
      <category>redux</category>
      <category>flux</category>
    </item>
    <item>
      <title>Digital Typewriter</title>
      <dc:creator>Alex Kotliarskyi</dc:creator>
      <pubDate>Sun, 09 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/frantic/digital-typewriter-43fb</link>
      <guid>https://dev.to/frantic/digital-typewriter-43fb</guid>
      <description>&lt;p&gt;What if you could use your favorite keyboard away from your computer?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ButSeexA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://frantic.im/assets/onityper/onityper.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ButSeexA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://frantic.im/assets/onityper/onityper.jpg" alt=""&gt;&lt;/a&gt;&lt;br&gt;
    This is what it looks like for now. Check out GIF demo further down.  &lt;/p&gt;

&lt;p&gt;A long time ago I learned about an interesting “text editor”. Its main feature was, uhh, the lack of text editing. That’s right, you can’t change anything you’ve typed.&lt;/p&gt;

&lt;p&gt;The idea behind it is very interesting. It’s about two modes of writing. One where you produce as much text as possible, without trying to organize or correct it in any way. The other one is where you edit and restructure the text until it’s consistent and easy to read.&lt;/p&gt;

&lt;p&gt;Doing the first part on modern computers is a challenge for me. I get distracted very easily. I’m also tempted to start editing right away, often times ending up in a loop rewriting a sentence over and over again.&lt;/p&gt;

&lt;p&gt;Old typewriters don’t have this problem: there’s no distractions and no way to edit the text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qryw939t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://frantic.im/assets/onityper/deleece-cook-1167525-unsplash.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qryw939t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://frantic.im/assets/onityper/deleece-cook-1167525-unsplash.jpg" alt=""&gt;&lt;/a&gt;&lt;br&gt;
    Doesn't support twitter, doesn't have the backspace key.  &lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;On my last birthday I got a tiny device, Onion Omega 2+. It’s like Rapsberry Pi, runs Linux, but built on different architecture and has its own set of accessories. It came with 21x7 chars OLED display extension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VTRZqGat--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://frantic.im/assets/onityper/omega.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VTRZqGat--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://frantic.im/assets/onityper/omega.jpg" alt=""&gt;&lt;/a&gt;&lt;br&gt;
    Onion Omega  &lt;/p&gt;

&lt;p&gt;The idea was to connect my favorite keyboard (HHKBPro2 type-S) via USB to the Omega2. Anything I type there should immediately show up on the built-in OLED display and sync to “the cloud”.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I bet I can hack this together with 1 line of bash.&lt;/p&gt;

&lt;p&gt;– me, two weeks ago&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I thought it would be trivial, just read the keystrokes, pipe them to OLED and a text file, sync the text file over Dropbox.&lt;/p&gt;

&lt;p&gt;However, it turned out to be much more complicated than that :)&lt;/p&gt;

&lt;p&gt;First, I couldn’t find a trivial way to read the text off of the keyboard. I was looking for something like &lt;code&gt;getc&lt;/code&gt; that can get data from a connected USB device, but no luck. Instead I found out that Linux has &lt;code&gt;/dev/input/event*&lt;/code&gt; files where I could &lt;a href="https://www.kernel.org/doc/Documentation/input/input.txt"&gt;read raw events&lt;/a&gt;. That lead me to the ancient &lt;a href="https://github.com/spotify/linux/blob/master/include/linux/input.h"&gt;&lt;code&gt;input.h&lt;/code&gt;&lt;/a&gt;, which I used to hack together a simple parser.&lt;/p&gt;

&lt;p&gt;Next, printing this stuff to OLED also had gotchas. My initial version would just repaint the screen on every keystroke, which turned out to be very inefficient. Full display repaint using Omega’s OLED APIs takes as much as 400ms, totally unacceptable for a good experience.&lt;/p&gt;

&lt;p&gt;So I changed the code to keep track of cursor position and advanced it as more characters were being printed on the screen.&lt;/p&gt;

&lt;p&gt;This got more complicated once I added support for backspace. And then even more complicated with paging. When I got to implementing word wrapping, it was too much — there are so many different states to transition between!&lt;/p&gt;

&lt;p&gt;Ideally, I don’t want to track the current screen position and code the drawing logic for each editing command. I want a model where I can define piece of state (the text) and a function that defines what this should look like on the screen, without worrying about the underlying OLED API performance.&lt;/p&gt;

&lt;p&gt;Does it remind you of anything? :)&lt;/p&gt;

&lt;p&gt;React. I love this model and I’m sure there are tons of applications for it outside the web development. Here’s what my code looks like now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def render(text)
  # Return the text I want to be on the screen
end

next_screen = render(message)
reconcile(prev_screen, next_screen) do |row, col, text|
  oled_command "cursor", "#{row},#{col}", "write", text
end
prev_screen = next_screen

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The latency between pressing a button and seeing it on the screen is amazing, I’ll look into measuring it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6AVSvEGG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/http://frantic.im/assets/onityper/demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6AVSvEGG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/http://frantic.im/assets/onityper/demo.gif" alt=""&gt;&lt;/a&gt;&lt;br&gt;
    Demo  &lt;/p&gt;
&lt;h2&gt;
  
  
  Sync
&lt;/h2&gt;

&lt;p&gt;Everything I type on this device is stored in text files. It’s not very easy to pull these files from the device, so I also added some code to upload each line to the server.&lt;/p&gt;

&lt;p&gt;On the backend I used Firebase. I have a simple “cloud function” that takes text as input and stores it in Firestore. I also have a very simple frontend that renders the text from the Firestore.&lt;/p&gt;

&lt;p&gt;The tricky part is that I can’t completely rely on available internet connection. I plan to take this device to coffee shops and places outside of WiFi reach. The script stores a queue of lines to upload in a file, so even after device restarts it should be able to resume uploads.&lt;/p&gt;

&lt;p&gt;Ideally I’d love to use Dropbox, but unfortunately I couldn’t find linux MIPS build of their client.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;In retrospective I should have started with proper Ruby tests, but yak shaving and curiosity brought me to &lt;a href="https://pypi.org/project/cram/"&gt;&lt;code&gt;cram&lt;/code&gt;&lt;/a&gt;. It’s a simple CLI tool that takes &lt;code&gt;*.t&lt;/code&gt; files as inputs. These files contain shell commands and the expected output.&lt;/p&gt;

&lt;p&gt;The development experience of &lt;code&gt;cram&lt;/code&gt; is pretty good. You don’t need to set the “expected output” beforehand. You can just run the tool and it will show the difference between expected and actual output, with an option to update the &lt;code&gt;*.t&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Very similar to Jest snapshot tests.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Omega’s WiFi has been terrible in my experience, lots of random disconnects. I couldn’t rely on SSH because of that and had to use serial port with &lt;code&gt;screen&lt;/code&gt;. This made the deployment process more complicated, there’s no &lt;code&gt;scp&lt;/code&gt; for terminal connection.&lt;/p&gt;

&lt;p&gt;Here’s a clever trick I learned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -S $SESSION -X stuff 'cat &amp;gt; /root/onityper.rb &amp;lt;&amp;lt;\EOF'$'\015'
screen -S $SESSION -X readreg p "$ROOT/onityper.rb"
screen -S $SESSION -X paste p
screen -S $SESSION -X stuff 'EOF'$'\015'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can run &lt;code&gt;screen&lt;/code&gt; in one tab and while the connection is active I can send commands to it. &lt;code&gt;stuff&lt;/code&gt; sends keystrokes and &lt;code&gt;readreg&lt;/code&gt;/&lt;code&gt;paste&lt;/code&gt; can simulate typing large chunks of text.&lt;/p&gt;

&lt;p&gt;To run the script on every boot, I used &lt;a href="https://docs.onion.io/omega2-docs/running-a-command-on-boot.html"&gt;the official instructions&lt;/a&gt; which recommend putting stuff into &lt;code&gt;/etc/rc.local&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ruby /root/onityper.rb &amp;gt;&amp;gt; /tmp/onityper.log 2&amp;gt;&amp;amp;1 &amp;amp;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don’t like this at all. There must be a proper way of defining a service that can run in background, with policies about logs and log rotation, when to restart it, etc. Looks like OpenWRT has &lt;a href="https://openwrt.org/docs/guide-developer/procd-init-scripts"&gt;&lt;code&gt;procd&lt;/code&gt;&lt;/a&gt; but at that point I was too lazy to look into.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Warning: it’s a bag of hacks. See it on &lt;a href="https://github.com/frantic/onityper"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;I’m very happy with this setup so far. I bought the cheapest powerbank I could find on Amazon, so the device is pretty much independent from my laptop. 70% of this post was created on the Onityper from a coffee shop.&lt;/p&gt;

&lt;p&gt;Now I want to use a 3D printer to build a case for it.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to convince your boss to use React Native</title>
      <dc:creator>Alex Kotliarskyi</dc:creator>
      <pubDate>Sat, 09 Mar 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/frantic/how-to-convince-your-boss-to-use-reactnative-4a2</link>
      <guid>https://dev.to/frantic/how-to-convince-your-boss-to-use-reactnative-4a2</guid>
      <description>&lt;p&gt;Likely you landed on this article because you are excited about React Native. Awesome! If you have a team of web engineers and a brand new app, it’s easy to make a case for starting with React Native.&lt;/p&gt;

&lt;p&gt;However, most companies already have apps in the stores. They used native stack to build their apps: Xcode or Android Studio. React Native can add value for these companies too, but making a hybrid app comes with some challenges.&lt;/p&gt;

&lt;p&gt;During the last 4 years I helped integrate React Native into Facebook and Oculus apps. Here are a few lessons I learned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Ffrantic.im%2Fassets%2Fgunnar-sigurdarson-1368301-unsplash.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Ffrantic.im%2Fassets%2Fgunnar-sigurdarson-1368301-unsplash.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Start with Empathy
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Wait, start with what?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Understand the people who work on your project—that’s the first step to successful integration.&lt;/p&gt;

&lt;p&gt;Your boss (project manager, director, CTO, etc.) is responsible for the app hitting the market with little risk, short timeline and maximum impact. Companies already operate in very competitive environment. To them any new technology is a risk.&lt;/p&gt;

&lt;p&gt;Your colleagues have a different perspective as well. They have experience building apps with the “native” technology stack. They know how to format strings, build UIs, access network, write and debug code in the IDE. Switching to a completely different ecosystem is very uncomfortable. There’s natural resistance to getting out of the comfort zone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Ffrantic.im%2Fassets%2Fmaximilian-weisbecker-544039-unsplash.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Ffrantic.im%2Fassets%2Fmaximilian-weisbecker-544039-unsplash.jpg"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Does React Native have layout inflaters? How do I change the text of this button?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Engineers might have existing opinions about React, React Native and JavaScript ecosystem. Or they’ve seen a React Native app somewhere and found it of bad quality. There are many common myths, e.g. “JS is slow”, or “frameworks come and go too fast”.&lt;/p&gt;

&lt;p&gt;These myths are not specific to JavaScript. As humans we can’t experience every single tech stack and have a well-formed opinion. We have to rely on things we’ve heard from others.&lt;/p&gt;

&lt;p&gt;This might not sound very encouraging, but &lt;strong&gt;be willing to accept a defeat&lt;/strong&gt;. There are so many scenarios where React Native can be very beneficial. But there are also valid situations where it doesn’t make sense at a particular time. And it’s okay. Maybe in a few months you can revisit this decision. The last thing you want is to polarize your team and ruin constructive work relationships.&lt;/p&gt;

&lt;p&gt;To fight existing misconceptions, prove them wrong on a real feature inside your app (without making other people feel stupid).&lt;/p&gt;

&lt;h1&gt;
  
  
  Find value
&lt;/h1&gt;

&lt;p&gt;This guide assumes you are integrating React Native into an existing application and already have a big chunk of functionality written using platform-dependent code. Rewriting it all is a huge risk with very little benefit to your users and business. That’s why it will be extremely hard to convince your team and people responsible for the project to do so.&lt;/p&gt;

&lt;p&gt;But you don’t have to rewrite your application! &lt;strong&gt;React Native can be adopted incrementally&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Find an area in your application that will immediately get benefits from being implemented in React Native. For example, it could be a screen that is useful but but doesn’t get much development attention or a surface that’s implemented as a WebView. For example, Facebook started with Pokes, Instagram rebuilt “liked photos”, etc.&lt;/p&gt;

&lt;p&gt;It has to be with a scope that you can build yourself within few days. It will be a great campground to test different aspects of integration: build system, session sharing, logging, crash reporting, etc.&lt;/p&gt;

&lt;p&gt;Bonus points for making the new feature run on both iOS and Android. Suddenly, something that used to take a lot of engineering time and coordination, was created by one person in a short time frame.&lt;/p&gt;

&lt;p&gt;Show, don’t tell.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Ffrantic.im%2Fassets%2Fhilthart-pedersen-602249-unsplash.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Ffrantic.im%2Fassets%2Fhilthart-pedersen-602249-unsplash.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Be a champion
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;You can’t just say “Let’s use React Native” and hide in the bushes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tc39" rel="noopener noreferrer"&gt;TC39&lt;/a&gt;, a committee that evolves JavaScript the language, has &lt;a href="https://tc39.github.io/process-document/" rel="noopener noreferrer"&gt;the following process&lt;/a&gt;: Changes to the language are called “proposals”. A member that leads a proposal is called a “champion”. Their mission is to create spec drafts, work with the community and push the spec forward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Become the React Native champion at your company.&lt;/strong&gt; Take the responsibility to see this idea through.&lt;/p&gt;

&lt;p&gt;The first step to becoming a champion is to get more familiar with React Native. Create a few prototypes (by &lt;a href="https://www.youtube.com/watch?v=I8b0v0uFXLs" rel="noopener noreferrer"&gt;rebuilding some parts of your app&lt;/a&gt;), learn architecture, be ready to give demos and help people get started.&lt;/p&gt;

&lt;p&gt;Engineers who are coming from native will most likely feel uncomfortable and overwhelmed by the new ecosystem. Create an environment where getting started with React Native is super easy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have an internal wiki page with &lt;a href="https://bitbucket.org/frantic/react-bnb" rel="noopener noreferrer"&gt;step-by-step instructions&lt;/a&gt; on how to get started. Think of it as a “landing page” of React Native in your organization.&lt;/li&gt;
&lt;li&gt;Build a script that automates environment setup: installs NodeJS and other dependencies, runs yarn install, etc.&lt;/li&gt;
&lt;li&gt;Setup good IDE defaults. You can’t ask IntelliJ users to configure and use VIM. Document or commit configs that make it much easier to get started with React Native. For example: editor recommendations, plugins, syntax schemes, keyboard shortcuts, etc.&lt;/li&gt;
&lt;li&gt;Organize a tech talk or a small hackathon where your team can learn about React Native and try it together on a small project.&lt;/li&gt;
&lt;li&gt;Create a place to ask questions, have discussions and hang out. Maybe a &lt;code&gt;#react_native&lt;/code&gt; channel on Slack, group, email list, etc. Be there to answer questions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Super important: if there are people who don’t want to deal with React Native, &lt;strong&gt;make sure their development experience is not compromised&lt;/strong&gt;. They should be able to work the same way as before without having to know or do anything about React Native.&lt;/p&gt;

&lt;h1&gt;
  
  
  Closing thoughts
&lt;/h1&gt;

&lt;p&gt;If you want to convince somebody to use React Native, ship a feature in your app that wouldn’t have been possible without it.&lt;/p&gt;

&lt;p&gt;I hope this small guide gives you few ideas on how to get started. It’s only the beginning.&lt;/p&gt;

&lt;p&gt;Lots of big and small companies use React Native in their hybrid apps. Unfortunately, &lt;a href="https://medium.com/airbnb-engineering/react-native-at-airbnb-f95aa460be1c" rel="noopener noreferrer"&gt;very&lt;/a&gt; &lt;a href="https://eng.uber.com/ubereats-react-native/" rel="noopener noreferrer"&gt;few&lt;/a&gt; of them share the details. I think it’s mostly because some aspects of the integration are specific to their infrastructure and it doesn’t feel useful to talk about these.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>react</category>
      <category>empathy</category>
    </item>
  </channel>
</rss>
