<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Power the People - Medium]]></title>
        <description><![CDATA[the Tech Behind the Solar Revolution in Africa - Medium]]></description>
        <link>https://medium.com/oge-tech?source=rss----483b3493b10f---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Power the People - Medium</title>
            <link>https://medium.com/oge-tech?source=rss----483b3493b10f---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 08:42:54 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/oge-tech" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[My Experience Working on Flutter for Integration Testing]]></title>
            <link>https://medium.com/oge-tech/my-experience-working-on-flutter-for-integration-testing-8d0e8f5504c0?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/8d0e8f5504c0</guid>
            <category><![CDATA[software-testing]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[automated-testing]]></category>
            <dc:creator><![CDATA[Tomi Familoni]]></dc:creator>
            <pubDate>Tue, 14 May 2019 10:42:15 GMT</pubDate>
            <atom:updated>2019-05-14T10:42:15.210Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>My Experience Working on Flutter for Integration Testing</strong></p><p>I recently joined Zola Electric as a QA Engineer and the Software Development team currently leverages the Flutter SDK to develop their customer-facing Android and iOS apps. The choice of the cross-platform mobile development framework was a no-brainer; Flutter’s hot reload feature, support for beautiful Material Design and Flutter’s ability to incorporate all critical platform differences to provide full native performance are the common reasons for the choice. But then, my fear was how to automate tests for these mobile apps.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*y9VG4t68aVey2emXaKL90A.jpeg" /><figcaption>Photo by <a href="https://www.pexels.com/@rawpixel?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels">rawpixel.com</a> from <a href="https://www.pexels.com/photo/white-laptop-computer-on-white-desk-1329068/?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels">Pexels</a></figcaption></figure><p>First and foremost, testing using the Flutter driver was a bit different from Appium which I was accustomed to. In Flutter, automated testing falls into three categories; Unit testing, Widget testing, and Integration testing. Unit tests and Widget tests focus on individual classes, functions, or Widgets. However, integration testing tests how the performance of an application runs on a real device. This includes testing how the app behaves or responds to a tap, scroll, text input, etc. Integration tests can be automated to run on an emulator.</p><p>Secondly, it meant learning a new language; <a href="https://www.dartlang.org/">Dart</a> developed by Google. It was not so difficult writing my first few tests because I was familiar with Java. In order to be a bit more comfortable with the language and framework, what I did was:</p><ul><li>create a simple dummy app then set up Flutter driver</li><li>collate all Serialized Finders (objects repository) in a separate file</li><li>write a few tests using this as a <a href="http://cogitas.net/write-integration-test-flutter/">guide</a></li></ul><p>Then I ran the test. Imagine my reaction when they all passed!</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgiphy.com%2Fembed%2FiMBEVqJRJkEQC5EGa2%2Ftwitter%2Fiframe&amp;display_name=Giphy&amp;url=https%3A%2F%2Fmedia.giphy.com%2Fmedia%2FiMBEVqJRJkEQC5EGa2%2Fgiphy.gif&amp;image=https%3A%2F%2Fi.giphy.com%2Fmedia%2FiMBEVqJRJkEQC5EGa2%2Fgiphy-downsized-large.gif&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=giphy" width="435" height="244" frameborder="0" scrolling="no"><a href="https://medium.com/media/4f9f42b3a05acdb1baa8995a1406a639/href">https://medium.com/media/4f9f42b3a05acdb1baa8995a1406a639/href</a></iframe><p>Another thing was that I was required to share the same repository with the developer. Before now, my experience at my previous companies was that it was better not to co-locate app and automation code as it was assumed that it was too much of a risk to allow testers to have access to the ‘almighty’ app code. My other fear was sharing the same repository with a developer who was no ordinary developer at all! She is Annyce Davis, an Android Google Developer Expert, Software Group Lead at Zola Electric and my line manager.</p><p>I was initially afraid that I would not be able to write intelligent code. Turns out, there wasn’t any reason for such fear as it has been a great experience. I am even getting better at it and getting more familiar with the internal workings of the project. Other advantages I have actually experienced are; unreliable web element locators that cause flaky automated tests are avoided and that it also makes each party more involved/carried along with the other’s contribution to the repository. With this experience, there are many other advantages that can be derived from this which can be seen <a href="https://techbeacon.com/app-dev-testing/6-reasons-co-locate-your-app-automation-code">here</a>.</p><p>Also, Flutter’s support community isn’t so large yet at least compared to Appium’s. And so I made a few mistakes, figured out some things on my own and asked a lot of questions from the developer who was way more experienced using it and was glad to help.</p><p>So far, I’m enjoying working with Flutter. I’ll keep on practising because I have learned that <em>“practice is improvement, and improvement only leads to perfection”</em> ― Richelle E. Goodrich.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8d0e8f5504c0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/my-experience-working-on-flutter-for-integration-testing-8d0e8f5504c0">My Experience Working on Flutter for Integration Testing</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Aliasing Fields on Ecto Associations in Absinthe for GraphQL]]></title>
            <link>https://medium.com/oge-tech/aliasing-fields-on-ecto-associations-in-absinthe-for-graphql-abcaad92b9b4?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/abcaad92b9b4</guid>
            <category><![CDATA[absinthe]]></category>
            <category><![CDATA[graphql]]></category>
            <category><![CDATA[api]]></category>
            <category><![CDATA[elixir]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Tony Pitale]]></dc:creator>
            <pubDate>Fri, 08 Feb 2019 18:28:50 GMT</pubDate>
            <atom:updated>2019-02-08T18:28:49.917Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fB8_4qISXKPwexAOuN7sjA.png" /></figure><p>How do we maintain the consistency of our GraphQL APIs over time, while the underlying data structures change? This is a common question we face building software on the web. We have mobile and react applications, or third party consumers, that rely upon the structure and data provided our APIs. The truth of our data storage may be much different than the API presents. Let’s adapt a few common scenarios using features available from Absinthe in Elixir/Phoenix/Ecto.</p><p>One technique for handling this is to continue presenting the original field, but using data from another source. I would refer to this as “aliasing” or mapping one field’s data into another. The fundamental idea in Absinthe is that the name of the field presented in the API is not required to be the key for the source of data we use.</p><p>I’ll cover two scenarios of database refactorings I went through recently, while keeping the API backward compatible:</p><ol><li>Renaming a column</li><li>Moving a column into another table</li></ol><h3>Renaming a Column</h3><p>This is a simple example to get us warmed up, but we’ll use this technique in combination with another in our next example.</p><p>We’ll start with a table and a column (example from Postgres):</p><pre>CREATE TABLE firmware_versions (<br>  id SERIAL,<br>  semantic_version_number text<br>);</pre><pre>CREATE TABLE devices (<br>  id SERIAL,<br>  firmware_version_id integer REFERENCES firmware_versions(id),<br>  desired_firmware_version_id integer REFERENCES firmware_versions(id)<br>);</pre><p>And the Absinthe object type would look something like (I’ve omitted data loaders and resolvers):</p><pre><a href="http://twitter.com/desc">@desc</a> &quot;Firmware version&quot;<br>object :firmware_version do<br>  <a href="http://twitter.com/desc">@desc</a> &quot;Database ID of the firmware version&quot;<br>  field(:id, :integer)</pre><pre><a href="http://twitter.com/desc">@desc</a> &quot;Firmware version expressed as a semantic version number&quot;<br>  field(:semantic_version_number, :string)<br>end</pre><pre><a href="http://twitter.com/desc">@desc</a> &quot;A network-connected device&quot;<br>object :device do<br>  …<br>  <a href="http://twitter.com/desc">@desc</a> &quot;Current firmware version of the device&quot;<br>  field(:firmware_version, :firmware_version, resolve: dataloader(Device))</pre><pre><a href="http://twitter.com/desc">@desc</a> “Firmware version we are attempting to update to&quot;<br>  field(:desired_firmware_version, :firmware_version, resolve: dataloader(Device))<br>end</pre><h4>Changing the object_type</h4><p>What happens when we change the column name from desired_firmware_version_id to requested_firmware_version_id? This is an admittedly contrived example, meant to highlight a point. Aside from the normal database migration to alter the table, we need only make a straightforward change in our GraphQL type for the device to maintain the interface for our users.</p><pre>field(:requested_firmware_version, :firmware_version, resolve: dataloader(Device), name: &quot;desired_firmware_version&quot;)</pre><p>Two changes are required. To start, we update the first argument in field to match the new name of the column (in this case, an association). Then, we add the option for name and pass in a string (binary) to match the previous column’s name in the API.</p><p>If you are planning to deprecate the old field name over time, add the deprecate option available on field and pass it a string with the reason for the deprecation. Absinthe will handle the rest.</p><h3>Moving to a New Table</h3><p>What happens if we make a more drastic change to the structure of our data by adding a relation to the mix? Whether this is a new table or an existing table that better suits our data, our structures may change in more substantial ways. In this example, we’ll start from the same initial structure of the devices table from above. Instead, we’ll add a new table rather than renaming our column.</p><pre>CREATE TABLE firmware_update_requests (<br>  id SERIAL,<br>  pending boolean DEFAULT true,<br>  device_id integer REFERENCES devices(id),<br>  firmware_version_id integer REFERENCES firmware_versions(id)<br>);</pre><pre>CREATE UNIQUE INDEX pending_request_index ON firmware_update_requests(device_id, pending);</pre><p>This change will necessitate more changes than our previous example, but fundamentally, the technique is the same.</p><h4>Add an Ecto Virtual Field and Association</h4><p>To make it work, we just need to get the data from this new table’s firmware_version_id into a place on the device record where our alias Absinthe field can get to it.</p><p>We’ll use a combination of two Ecto features: we’ll make space for the firmware_version_id using a virtual field in the schema and add our association without defining a field (since we already did it in the virtual field).</p><pre># in MyApp.Device module<br>schema :devices do<br> # snip …<br> field(:requested_firmware_version_id, :integer, virtual: true)<br> belongs_to(:requested_firmware_version, MyApp.FirmwareVersion, define_field: false)<br>end</pre><p>This creates a place for us to join data into the device, and allows us to look it up as an association.</p><h4>Update the Absinthe Resolver</h4><p>Now, we can change our query to load this data. For Absinthe, we do this in the resolver:</p><pre>defp with_pending_request(query \\ MyApp.Device) do<br> from(<br>   d in query,<br>   left_join: r in MyApp.FirmwareUpdateRequest,<br>   on: d.id == r.device_id,<br>   on: r.pending == true,<br>   select: %{d | requested_firmware_version_id: r.firmware_version_id}<br> )<br>end</pre><p>Let’s break this down. We’ll start with the query argument. This is an Ecto.Query for records from MyApp.Device. Next, we left_join our firmware_update_request record on the matching device_id and we restrict it to pending requests so that we only join, at most, a single record for each device.</p><p>Lastly, my favorite bit, is the select. It merges the firmware_version_id into the new virtual field we added to the MyApp.Device schema. And with that data in place, we can use our previous change to the Absinthe object type field for the name option and everything will work.</p><h3>All Together Now</h3><p>With all of these changes: using the resolver to load the firmware version from the firmware_update_request association into an Ecto virtual field and aliasing the field in Absinthe we have successfully mapped a column from an associated table into place on the original field in our GraphQL API. Thus, we have retained the external structure and data of our API while modifying the underlying data store.</p><p>While this is how I solved this particular problem it is, of course, not the only way to solve this problem. Other solutions might involve resolver callbacks (coming in the next version of Absinthe) or simply a custom resolver that returns a properly formed map. Whatever path you choose, I hope this provides you some new info on both Absinthe and Ecto and their function.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=abcaad92b9b4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/aliasing-fields-on-ecto-associations-in-absinthe-for-graphql-abcaad92b9b4">Aliasing Fields on Ecto Associations in Absinthe for GraphQL</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Migrating to Kotlin Android Extensions]]></title>
            <link>https://medium.com/oge-tech/migrating-to-kotlin-android-extensions-3af7086a1285?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/3af7086a1285</guid>
            <category><![CDATA[kotlin-android-extensions]]></category>
            <category><![CDATA[butterknife]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <dc:creator><![CDATA[Moyinoluwa Adeyemi]]></dc:creator>
            <pubDate>Mon, 28 Jan 2019 10:44:13 GMT</pubDate>
            <atom:updated>2019-01-28T12:07:34.465Z</atom:updated>
            <content:encoded><![CDATA[<p>At Zola Electric, we recently migrated completely off ButterKnife to Kotlin Android Extensions and this article shows the common use-cases we had.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dtW_ct0tyh-ckTdSH7JZ2g.png" /><figcaption>Yuuuuuuuge PR!</figcaption></figure><p>ButterKnife was very useful for us while we had it until it wasn’t. Our gradle build speeds were slowing down and avoiding annotation processors was a small step in the right direction. We also were happening on a lot of errors that were usually resolved with a clean and rebuild, but sometimes required invalidating caches and restarting Android Studio.</p><p>There are a lot of articles that already explain what Kotlin Android Extensions are and how to set it up (I recommend <a href="https://antonioleiva.com/kotlin-android-extensions/">this article</a> by <a href="https://medium.com/u/a199eb0fa9a0">Antonio Leiva</a>) so I won’t be covering that here. We used Butterknife in a lot of different ways in our app—to bind values, bind views and set listeners — and I’ll explain how they were replaced in this article.</p><h3>Binding values (e.g. booleans, dimensions)</h3><p>Here’s an example of how we used the BindDimen annotation.</p><pre>@JvmField<br>@BindDimen(R.dimen.height)<br>var dialogSizePx: Int = 0</pre><pre>dialog.window?.setLayout(MATCH_PARENT, dialogSizePx)</pre><p>And here’s how it was replaced:</p><pre>dialog.window?.setLayout(MATCH_PARENT,<br>                         context?.dimen(R.dimen.height))</pre><h3>Binding views and setting listeners</h3><h4>In Activities</h4><p>We now call views in anActivity with their ids. The major things to watch out for are the listeners which were previously called with an annotation on a method but now have to be set manually.</p><p>Here’s how we did that with Butterknife:</p><pre>class MainActivity {<br><br>    override fun onCreate(savedInstanceState: Bundle?) {<br>        super.onCreate(savedInstanceState)<br>        setContentView(R.layout.activity_main)<br>    }<br><br>    @OnClick(R.id.<em>cancel_button</em>)     <br>    fun handleCancelClick() { <br>        // do something      <br>    }<br>}</pre><p>And how we do that now with the Kotlin Android Extensions:</p><pre>class MainActivity {<br><br>    override fun onCreate(savedInstanceState: Bundle?) {<br>        super.onCreate(savedInstanceState)<br>        setContentView(R.layout.activity_main)<br><br>        cancel_button.setOnClickListener <strong>{ </strong>handleCancelClick() <strong>}<br>    </strong>}</pre><pre>    fun handleCancelClick() { <br>        // do something      <br>    }<br>}</pre><h4>In Fragments</h4><p>This works the same way as in an Activity but with one major difference. A NullPointerException occurs whenever a view is called from the onCreateView method. All views should be called from the onViewCreated method instead.</p><h4>In Custom DialogFragments</h4><p>Even though the DialogFragment extends the Fragment, the onViewCreated method is never called by default. This is because the onViewCreated method should be called immediately after onCreateView but since the onCreateDialog is used instead in aDialogFragment then that never happens.</p><p>So, overriding the onViewCreated method here will not result in any action. Instead, we can override the getView method and pass in a containerView. This will allow us call the views normally from the onCreateDialog method.</p><pre>val containerView by lazy <strong>{<br>    </strong>inflate(context, R.layout.fragment_dialog, null) as ViewGroup<br><strong>}<br><br></strong>override fun getView() = containerView</pre><pre>override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {<br>    val builder = AlertDialog.Builder(requireContext())<br><br>    text_view.setOnFocusChangeListener <strong>{ </strong>v, _ <strong>-&gt; </strong>closeKeyboard(v) <strong>}<br><br>    </strong>builder.setView(containerView)<br><br>    return builder.create()<br>}</pre><h4>In Custom Views</h4><p>We implement a LayoutContainer and override the containerView. The layout is inflated in the initializer block and listeners are set in the onFinishInflate method.</p><pre>class LoginMethodView @JvmOverloads constructor(<br>        context: Context,<br>        attrs: AttributeSet? = null,<br>        defStyleAttr: Int = R.style.<em>AppTheme<br></em>) : ScrollView(context, attrs, defStyleAttr), LayoutContainer {<br><br>    override val containerView: View?<br>        get() = this<br><br>    init {<br>        inflate(context, R.layout.<em>view_login_method</em>, this)<br>    }<br><br>    public override fun onFinishInflate() {<br>        super.onFinishInflate()<br><br>        hello_button.setOnClickListener <strong>{ </strong>sayHello() <strong>}<br>    </strong>}<br>}</pre><h4>In ViewHolders</h4><p>We implement a LayoutContainer in the ViewHolder class, override the containerView in the constructor and handle any listeners with an initializer block.</p><p>With ButterKnife:</p><pre>inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v),    View.OnClickListener {<br><br>    @BindView(R.id.<em>appliance_image</em>) lateinit var applianceImage: ImageView<br><br>    init {<br>        ButterKnife.bind(this, v)<br>        v.setOnClickListener(this)<br>    }<br><br>    override fun onClick(v: View?) {<br>        // handle view click<br>    }<br>}</pre><p>With KAE:</p><pre>inner class ViewHolder(override val containerView: View)<br>    : RecyclerView.ViewHolder(containerView),<br>      View.OnClickListener,<br>      LayoutContainer {</pre><pre>init {<br>        containerView.setOnClickListener(this)<br>    }<br><br>    override fun onClick(v: View?) {<br>        // handle view click<br>    }<br>}</pre><p>This was a very huge change for us and it took some time to complete, but we believe it’s a step in the right direction. Have you found other ways to use the Kotlin Android Extensions? Please let us know in the comments!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3af7086a1285" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/migrating-to-kotlin-android-extensions-3af7086a1285">Migrating to Kotlin Android Extensions</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A portrait of ETL Error Handling]]></title>
            <link>https://medium.com/oge-tech/a-portrait-of-etl-error-handling-f4e6d77d095?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/f4e6d77d095</guid>
            <category><![CDATA[data]]></category>
            <category><![CDATA[etl-errors-handling]]></category>
            <dc:creator><![CDATA[Ernest Mwalusanya]]></dc:creator>
            <pubDate>Fri, 11 Jan 2019 16:17:37 GMT</pubDate>
            <atom:updated>2019-01-11T16:17:37.573Z</atom:updated>
            <content:encoded><![CDATA[<h4>Speaking of ETL Error Handling, this is how ZOLA Electric handled this problem.</h4><p>From previous <a href="https://tech.offgrid-electric.com/luigi-data-pipelines-setup-7b9200727156">blog</a>, you probably have a clear understanding of how Data Infrastructure looks like at ZOLA Electric.</p><p>In this blog I will describe steps and measures we used to fix ETL errors as well as best practices we have adopted. Before I give you a description on how things used to be and how we handled them, let me give you a complete picture of internal ETL performance at ZOLA Electric for the past one year.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F9uHBtR-ck-uKKUBoJOYiw.png" /><figcaption>ETL performance at ZOLA Electric.</figcaption></figure><p><strong>How it used to be</strong><br>When building a Data Infrastructure, things can easily go out of hand due to bad designs or bad anticipations from the ETL flows that can lead to errors.<br> <br>Few years ago at ZOLA Electric, the error rate in our ETL infrastructure was very low. This did not indicate that everything was running smoothly but it was due to many errors that happened without a proper way to capture them and provide notifications.</p><p>The picture below shows a low error rate before we started alerting on ETL errors.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/822/1*CxLm18zvyCMWs_GUODPsBQ.png" /><figcaption>Low error rates at ZOLA Electric before adopting ETL best practices.</figcaption></figure><p><strong>The Transition Period</strong><br>To fix problems on “how things used to be”, we had to do a number of things as a way to make sure that we have proper procedures to follow when designing and maintaining our ETL. Our transition period involved changes in following key areas:</p><p><strong><em>Tools and Tech side:</em></strong><br>1. Removed the problematic ETL parts (ones that became non-stable).</p><p>2. Introduced more advanced and easily maintained tools.</p><p>3. Started to frequently Review PR (Pull Requests) from dev codes.</p><p>4. Added automated tests that check for code quality and styles.</p><p>5. Introduced staging environment for testing all changes before pushing them to production. (This is where we captured most of errors and fix them).</p><p><strong><em>ETL Execution:</em></strong><br>1. Introduced explicit job dependencies that stop the entire chain of jobs if an error occurs in one of the dependencies.</p><p>2. Allowed jobs to retry in case of connection faults or network related errors.</p><p>3. Introduced jobs periodic runs.</p><p><strong><em>Communication:</em></strong><br>1. Produced more actionable alerts via e-mails or slack notifications.</p><p>2. Team members provide details on what, how and status of what they are fixing from reported errors.</p><p><strong><em>Documentation:</em></strong><br>1. Document frequent errors and best ways to avoid them and fix them plus possible ways to automate the process of detecting those errors and fixing them.</p><p>2. Redesigned parts that failed too often or had design issues.</p><p>3. Documented all parts of our ETL and how to maintain.</p><p>After establishing proper steps, things changed in our ETL. Picture below will show how things were during transition period. This is when we introduced proper alerts per each event in our ETL.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/798/1*SABK3MI7y_acGeljONXdPQ.png" /><figcaption>High error rates at ZOLA Electric during transition period</figcaption></figure><p>High error rate at transition period was mostly due to uncaptured errors on the old ETL infrastructure. The main improvement at this stage was having proper procedures to capture and fix all errors.</p><p>Fixing the majority of captured errors at this stage gave us a very good foundation to where we are now and where we want to be in future.</p><p><strong>Where we are and where we are going</strong></p><p>After introducing standard ways of design and maintaining ETL at ZOLA Electric, our overall ETL error rate dropped significantly.</p><p><strong><em>Current situation:</em></strong> More stable ETL with low error rate.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/790/1*Uv0jvZKc2AHjfRqN2BrHfw.png" /><figcaption>Low error rates after capture and handle properly all ETL errors.</figcaption></figure><p>With current design of our infrastructure, our ETL is more stable and whenever errors occur, they now get captured, fixed and documented. If they are caused by connection / network related faults they even get fixed automatically, with periodic retries. Other errors like bad syntax in code rarely reach production due to code checks we have before deployment and thorough tests on staging.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f4e6d77d095" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/a-portrait-of-etl-error-handling-f4e6d77d095">A portrait of ETL Error Handling</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Luigi Data Pipelines Setup]]></title>
            <link>https://medium.com/oge-tech/luigi-data-pipelines-setup-7b9200727156?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/7b9200727156</guid>
            <category><![CDATA[data-warehouse]]></category>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[luigi]]></category>
            <dc:creator><![CDATA[Konrad Burnik]]></dc:creator>
            <pubDate>Wed, 21 Nov 2018 10:58:29 GMT</pubDate>
            <atom:updated>2018-11-21T10:58:27.966Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*umL76uyK6pAbQ_5f" /><figcaption>Luigi. Source: pixabay.com</figcaption></figure><h4>Introduction</h4><p>At <a href="http://zolaelectric.com/">ZOLA Electric</a>, we use the <a href="https://www.python.org/">Python</a> <a href="https://github.com/spotify/luigi">Luigi framework</a> and <a href="https://docs.aws.amazon.com/redshift/latest/dg/c_redshift-sql.html">SQL</a> for building our data pipelines that load data to our Data Warehouse currently running on Redshift. Since our data volumes are not in the terabytes range, our existing system is organised mostly around Python that does the initial fetching and parsing of the data as well as the SQL code that is executed against <a href="https://aws.amazon.com/redshift/">Redshift</a> to structure the replicated tables into report-ready tables.</p><h4>Production and Staging Environments</h4><p>We use two Redshift environments, one for production one for staging. Here by staging environment I mean <em>testing environment</em>. So staging environment should not to be confused here with the <a href="https://en.wikipedia.org/wiki/Staging_(data)">staging area</a> of a typical Data Warehouse environment. Normally companies do not use a completely separate staging environment for their data warehouse since it is more work to maintain and also expensive for some. Usually, there are special schemas in production instead that are used for testing. We have found that having a separate database instance is very useful to have. It allows us to test new features without breaking the existing functionality. Yes, sometimes we need to sync the Staging but we do it with scripts so it is not so difficult. Other than these basic two configurations mentioned, we also keep local configurations which are used to test newly developed code.</p><h4>Our Luigi Setup</h4><p>We run a Luigi <a href="https://www.docker.com/">Docker</a> container on an <a href="https://aws.amazon.com/ec2/">EC2 instance</a>. The luigi.cfg.* files are getting copied over from an <a href="https://aws.amazon.com/s3/">Amazon S3</a> bucket upon deploy via <a href="https://jenkins.io/">Jenkins</a>. We have two separate Luigi config files, for each of the environments, Production and Staging. We have a dedicated <a href="https://aws.amazon.com/ebs/">EBS volume</a> for keeping scratch files during operation of Luigi tasks.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/813/1*YBoRUBL4qilb33vMIhGcFA.png" /><figcaption>Simplified data pipeline architecture at ZOLA</figcaption></figure><p>The Luigi jobs are scheduled via crontab to run on the container. We are also considering running those via <a href="https://jenkins.io/">Jenkins</a>. The Luigi container itself is running Python 2 but recently we have migrated all our code to run on Python 3 within a <a href="https://virtualenv.pypa.io/en/latest/">virtualenv</a>. Python dependencies are handled via single requirements.txt. More on that migration in a follow-up post.</p><h4>Luigi Visualiser and Task History</h4><p>For enabling the Luigi Task History and Visualiser we spawned a separate Postgres instance to keep the history. To enable tracking the visualiser history we had to set the following in ourluigi.cfg.* file:</p><pre>[core]</pre><pre>error-email:<br>email-type: plain<br>email-prefix: Production # or Staging<br>environment: production # or staging</pre><pre>logging_conf_file: config/logging.conf</pre><pre>[task_history]<br>enable: True<br>dbconnection: postgres:///…<br>pickle-path: /tmp/…</pre><pre>[scheduler]<br>record_task_history: True<br>state-path: /tmp/luigi-state.pickle<br>remove_delay: 86400</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*xWaS_v8kWI8bz-8J" /><figcaption>Luigi Visualiser at ZOLA Electric</figcaption></figure><h4>Replicating Data from Source Systems</h4><p>Each data source that we use is replicated to our Redshift in a separate schema. This is convenient for various organisational reasons, at the very least it simplifies the naming of tables and keeps the namespace neat. The replication job is a Luigi Task which runs through a <a href="http://yaml.org/">yaml</a> file which usually contains a whitelist of all the tables which we want to replicate along with a method of replication (full or incremental) and then replicates them by using S3 as an intermediate.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/709/1*k4obo5jgcPNIcduDbxXTeg.png" /><figcaption>The most basic replication into our Data Warehouse</figcaption></figure><h4>Structuring Data into Data Warehouse Tables</h4><p>Starting from replicated tables, we aim to create fact tables, which store periodic snapshots of our most used entities from our in-house developed operations management system. When needed, the fact tables are reloaded via master tables which keep history. The fact tables are not part of proper <a href="https://en.wikipedia.org/wiki/Star_schema">star-schemas</a>, as we do not use any dimension tables. Instead, we store the dimension and fact attributes in a single table row of a fact table. In that sense, our fact table layer consists of <a href="https://www.westmonroepartners.com/~/media/Files/White-Papers/Dimensionless-modeling.pdf">dimensionless fact tables/flat star-schemas</a> which are then used by <a href="https://looker.com/">Looker</a>. We found that this approach significantly simplifies our data pipeline logic, as well as table development and maintenance. For other sources which do not keep history themselves, we store the history in DWH in separate schemas by doing a periodic pull from them. Overall, we have four different types of tables in our Data Warehouse:</p><ul><li>replicated source data — replicated data from external source systems, which are either RDS or API-based, separated into schemas. Mostly Python code is used for this replication.</li><li>reference — internal lookup tables, used for reporting labels etc.</li><li>extended — calculated data for source tables, usually copies of source data with added calculated columns. Also data preprocessed by Python is loaded here.</li><li>fact — daily snapshots of combinations of source data tables, used as main tables for reporting.</li></ul><h4>Organising SQL Code</h4><p>Starting from the replicated tables, we use a combination of Python and SQL to populate the reference, extended and fact tables. Usually there are many SQL scripts involved, all of which have some dependencies among each other, for example one file needs to be executed before some other file, etc. Luigi is great for handling those kinds of dependencies between tasks. Some useful functionalities were added to the plain Redshift SQL scripts via <em>annotations</em>.</p><p>The annotations are comments in the top part of the SQL file which are then interpreted with our in-house Luigi tasks prior to executing the SQL code itself. We call this enriched SQL code as well as the framework that executes it <em>FreeSQL</em> due to historic reasons. It is in fact a legacy on top of which we built additional functionality as it proved very useful for defining SQL based transformations.</p><p>Most of our fact table preparation jobs are FreeSQL code which have indicated in their header via annotations a list of other FreeSQL files as dependencies that should run prior to the main script. As mentioned we added many other features to FreeSQL but this is out of scope of this post.</p><p>Here is an example of a simple FreeSQL script load_table_c.sql:</p><pre>-- requires:<br>--  files:<br>--   load_table_a.sql:<br>--   load_table_b.sql:</pre><pre>insert into table_c(c1, c2, c3)<br> select a.c1, b.c2, b.c3<br>  from table_a as a<br>  join table_b as b on a.id = b.id;</pre><p>In this example, the load_table_a.sql and load_table_b.sql will be executed first as indicated by the comment in the header. After this, the main insert statement will be called. This script will be expanded by our FreeSQL framework into the following Luigi Task <a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph">DAG</a>, and then executed:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G2tS6Fu-fg3Mtcs4CJdSdw.png" /><figcaption>A basic FreeSQL in action.</figcaption></figure><p>Our Python-based replication and parsing tasks are also organised into DAGs but these are often generated with Python and involving many steps, like for example.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*D4Kd9-Qz0Z-81KWY" /><figcaption>Luigi parsing our Device data into our Data Warehouse tables.</figcaption></figure><h4>Handling Errors and Alerting</h4><p>Every time a Luigi task fails, we get an e-mail message and also sometimes for more severe issues, a Slack alert. More details about error handling are coming in the following posts.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*mZXXaKxdu9XGAJWF" /><figcaption>Luigi could not access our Redshift cluster. Oops.</figcaption></figure><h4>Testing Python Code</h4><p>We use <a href="https://docs.pytest.org/en/latest/#">PyTest</a> four running all our Python tests. On each deploy of our Luigi docker container we run these tests. We also have support for testing FreeSQL code, but that is a topic for another post. For test coverage we use <a href="https://pypi.org/project/pytest-cov/">pytest-coverage</a>. We are running the complete test suite once a week and then sending the obtained test coverage report via Slack.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*YfkO0vD3RPI3AOu2" /><figcaption>A Slack message containing a link to the unit-test coverage report. Luigi is happy! Image source: <a href="https://www.mariowiki.com/Luigi">https://www.mariowiki.com/Luigi</a></figcaption></figure><h4>Self-service Luigi tasks at ZOLA</h4><p>Besides the scheduled Luigi jobs, sometimes certain tables in the Data Warehouse need to be refreshed manually by the BI team. For these purposes we developed a simple Jenkins job called Luigi Runner that can be used to trigger Luigi jobs manually.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*doI9ALfDxN6XwRnLNBroCw.png" /><figcaption>Self-service Luigi Tasks via Jenkins.</figcaption></figure><p>How this Luigi Runner utility works in detail will also be a topic for a future blog post.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/256/1*PH7uEtNnb8DI0uBrvOollA.png" /><figcaption>That’s all folks! Image source: <a href="https://www.mariowiki.com/Luigi">https://www.mariowiki.com/Luigi</a></figcaption></figure><p><em>At ZOLA Electric our data pipelines are based on the Python Luigi framework and SQL running against Redshift. In the following blog posts we will dive into more detail, so stay tuned!</em></p><p>Hope you enjoyed this post! Have any questions or thoughts? Let us know in the comments!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7b9200727156" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/luigi-data-pipelines-setup-7b9200727156">Luigi Data Pipelines Setup</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Better than the whiteboard?]]></title>
            <link>https://medium.com/oge-tech/better-than-the-whiteboard-d2936a59c51d?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/d2936a59c51d</guid>
            <category><![CDATA[junior-developer]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[interview]]></category>
            <category><![CDATA[remote-working]]></category>
            <dc:creator><![CDATA[Moyinoluwa Adeyemi]]></dc:creator>
            <pubDate>Tue, 20 Nov 2018 18:30:27 GMT</pubDate>
            <atom:updated>2018-11-20T18:30:26.591Z</atom:updated>
            <content:encoded><![CDATA[<p>There is no one way to conduct a technical interview. People have often used the dreaded whiteboard interviews, take-home coding assignments and even reviewed existing code on Github. There is a lot of backlash against the whiteboard coding interview, understandably so, as that rarely portrays the day-to-day responsibility of the applicant.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RHMQ6A8vnaH0F_E1ABsiAA.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/photos/xHaZ5BW9AY0?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Thought Catalog</a> on <a href="https://unsplash.com/search/photos/whiteboard?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>At Zola Electric, we were recently looking to hire a Junior Android Developer and this meant we had to conduct interviews for a couple of applicants. Since this was a Junior role, we had no major expectations. We were looking for people who had basic experience with Android development, some source control and testing knowledge, great communication skills and were looking to level up their development skills.</p><p>There was an initial phone screen where candidates were shortlisted based on the above criteria and after that, the Android team (which consists only of <a href="https://medium.com/u/9f96e9614042">Annyce Davis</a> and I) decided on a pair programming session. Before this, I had never conducted an interview of this nature but the aim resonated with me. Since we were going to be working closely with this person everyday, it was best to see what a pair programming session with them would feel like as this was going to happen a lot if they were hired. I was a bit nervous before the interviews started and had to remind myself who the interviewer was even though <em>I </em>was the one typing the code. 🙈</p><p>Every interview session was conducted remotely via <a href="https://zoom.us/">Zoom</a> and started with me sharing my screen with the applicant and explaining what our app did. Then I picked up a ticket from <a href="https://www.atlassian.com/software/jira">Jira</a> and walked through the description and acceptance criteria with them. I also opened up the designs for the ticket on <a href="https://zeplin.io/">Zeplin</a> to ensure that we fully understood all the changes we needed to make to the app, down to the dimensions. I did all of this before opening up Android Studio. Because I wanted the whole process to feel less like an interview and more like a typical day at my job, we googled some things and checked the Android Developer documentation together as this is a very common occurrence for me. There was time for some testing before wrapping up the one-hour session.</p><p>Just from us attempting to implement the ticket together and me asking them a few questions, I was able to learn a lot about each candidate and was delighted to receive implementation suggestions I didn’t think of myself. Here, I was looking out for a knowledge of Android Studio, basic knowledge of testing, good questions asked and their reasoning ability. Apart from assessing the candidate, my other objective was to ensure that they learnt something in that time frame even if they can’t all be hired. From the feedback I received from all of them, they all did and that’s a win-win on both ends.</p><p>I think we were able to pull this off because we were interviewing for a Junior Android Developer role. It will be interesting to see how this can be adapted to more senior positions.</p><p>Have you ever interviewed or been interviewed in this way? How did it go? Please let me know in the comments!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d2936a59c51d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/better-than-the-whiteboard-d2936a59c51d">Better than the whiteboard?</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Help us Power the World with Tech]]></title>
            <link>https://medium.com/oge-tech/help-us-power-the-world-with-tech-7d947fa03bc1?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/7d947fa03bc1</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[jobs]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[solar-energy]]></category>
            <category><![CDATA[nigeria]]></category>
            <dc:creator><![CDATA[Annyce Davis]]></dc:creator>
            <pubDate>Mon, 15 Oct 2018 20:20:19 GMT</pubDate>
            <atom:updated>2018-10-15T20:20:18.888Z</atom:updated>
            <content:encoded><![CDATA[<h4>Join us as our Jr Android Developer (Lagos, Nigeria)</h4><p>You will fall in love with this role if you can get passionate about bringing solar energy-as-a-service to the off-grid market (over 1.3 billion people around the world) and the unique social, technical, and business challenges that come along with it. Naturally, you’ll be eager for adventure, inspired by the opportunity to unleash whole continents of human potential, and motivated to help build a truly epic business.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*miBcsHegSv5bG9K5BIV9Zg.jpeg" /></figure><p>As a core member of the Digital Platforms department, you’ll help define and create new product features and experiences for our rapidly growing customer base and workforce. Our team is responsible for Zola Electric’s core platform, our “operating system for the business.” The Zola platform provides much of the magic behind the three key areas of our integrated operation:</p><ul><li>Leading our distributed workforce: automatically dispatching and coordinating the activities of thousands of field staff in multiple countries</li><li>Customer payments and service: enabling all mobile payment transactions and delightful customer interactions for our growing customer base</li><li>Distribution and delivery of solar systems: handling the movement of massive numbers of solar systems and appliances such as low energy TVs from factories through home installation.</li></ul><p>Zola Electric is creating opportunities for thousands of local workers to participate in a high tech startup, and our platform enables us to do this with utmost efficiency, scale, and reliability.</p><h3>The Role</h3><p>As a<a href="https://cezanneondemand.intervieweb.it/offgrid-electric/jobs/junior_android_developer_4289/en/"> <strong>Junior Android Developer</strong></a>, you will help drive the evolution of our mobile application as the company accelerates into hypergrowth mode. This includes implementing new features to enhance existing user flows, as well as fixing bugs which hinder successful user journeys. This would include a basic understanding of our APIs and how to work successfully within their constraints.</p><p>You’ll be working to solve interesting technical and human problems, such as:</p><ul><li>Implementing an adaptive user interface to accommodate a range of user roles</li><li>Ensuring that our app elegantly handles a range of data network access scenarios</li><li>Collaborating effectively in a distributed team, using a pragmatic, Agile process</li></ul><h3>Job Requirements</h3><ul><li>Initial experience with Android</li><li>Experience with Java or Kotlin</li><li>Initial experience with version control (Git, Github, etc)</li><li>An example project that can be shared considered a bonus, if available</li><li>A Computer Science degree is not required. Experience in internships or alternative career paths are accepted</li></ul><h3>What Zola Electric Offers</h3><p>How Zola Electric will set you up for success, we have been growing and developing our team plans, processes, and support structures for years. We are looking to add a small new team together in Nigeria to come onboard and support each other all at the same time.</p><ul><li>Competitive Salary</li><li>Access to the best machines, tools, and internet around</li><li>Paid Conference budget, and paid training materials available to you</li><li>Each hire assigned an experienced buddy/mentor</li><li>Working with existing Technical and Operations Guides on the ground</li><li>Career Guidance and Growth</li><li>Pair Programming Time and Support</li><li>Internal Dev talks, learn from our talks and develop your technical speaking skills</li><li>Travel opportunities to work with our globally distributed team in person</li></ul><h3>What You Bring</h3><p><strong>Always be Learning.</strong> You are committed to self-improvement, which means you’ve sought and found mentors to learn from. You’re also a natural at collaborative learning including reviewing others’ work, participating in design discussions, improving team code quality, and bring new ideas to the team. Because you know there’s always more to learn, you don’t get hung up on knowing everything before taking action. As you know so well, taking action is often the only way to discover if something works.</p><p><strong>Technical beginnings.</strong> You have worked with and explored several frameworks and languages. You have started to develop favorites but enjoy learning and digging into new things. You want to dive in on both smaller and newer applications and large applications with years of operational experience under the hood.</p><p><strong>User-centered.</strong> As much as you love technical challenges for their own sake, you’re inspired by making things that work for real people. You ask tough questions about how requested features solve user problems, and have a great track record of collaborating with designers and product managers on solutions.</p><p><strong>Effortless Communicator.</strong> You love to work as part of a distributed team. You realize that the key to successful remote work is trust, which comes from regular communication through tools like Slack, and respect for the team’s Agile process (e.g., Jira stories and GitHub pull requests). You also know the value of face-to-face interaction, even if only through web conferencing tools, and have enjoyed being a great teammate in virtualized contexts.</p><p><strong>Pragmatic.</strong> In a discussion of how to build a feature you will reliably bring people back to the question of what are we trying to achieve, and how will we know when we’re successful? You love new technologies, but will only recommend them if they are the most effective way of solving the problem at hand, and are an expert in playing a devil’s advocate of your own ideas. You are drawn to measure the results of changes to user experiences and look for opportunities to remove features if they are not being used (or used well).</p><p><strong>Curious.</strong> Unsatisfied with accepted wisdom, you’ll ask why multiple times when wrapping your head around a new concept or proposal. You’re also fascinated with the kinds of problems your peers in the industry are solving, and the novel approaches they’re taking. This leads you to be involved in local user meetings, online communities, and conferences.</p><h4>Other information</h4><p>You can learn more about us on <a href="https://twitter.com/offgride">Twitter</a>, <a href="https://www.facebook.com/offgridelectric/">Facebook</a>, and our <a href="http://zolaelectric.com/">Website</a>. If you are interested in joining our team in <strong>Lagos, Nigeria</strong>, you can submit your resume via our <a href="https://cezanneondemand.intervieweb.it/offgrid-electric/jobs/junior_android_developer_4289/en/">career page</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7d947fa03bc1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/help-us-power-the-world-with-tech-7d947fa03bc1">Help us Power the World with Tech</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Help us solve the developing world’s energy needs with software]]></title>
            <link>https://medium.com/oge-tech/help-us-solve-the-developing-worlds-energy-needs-with-software-6e67e03853c0?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/6e67e03853c0</guid>
            <category><![CDATA[jobs]]></category>
            <category><![CDATA[ruby-on-rails]]></category>
            <category><![CDATA[solar-energy]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[ruby]]></category>
            <dc:creator><![CDATA[Dan Mayer]]></dc:creator>
            <pubDate>Fri, 12 Oct 2018 19:05:27 GMT</pubDate>
            <atom:updated>2018-10-12T19:05:27.400Z</atom:updated>
            <content:encoded><![CDATA[<h4>Join us as our Sr Rails Engineer, Platforms</h4><p>Zola Electric is the leader in providing sustainable, affordable energy solutions for the world’s most complex markets. Today we deliver solar energy to nearly a million people in Sub-Saharan Africa, enabled by innovative hardware and software. Our team is responsible for Zola Electric’s core software platform–the operating system for the business–and the applications and data tools that sit on top of it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*miBcsHegSv5bG9K5BIV9Zg.jpeg" /><figcaption>our team installing a panel onto a customers roof</figcaption></figure><p>As a senior technologist in the Digital Platforms department, you’ll be instrumental in architecting and developing the systems that enable our rapidly growing international customer base and workforce. You will drive the primary architecture and design of our centralized API and integrations application.</p><h4>Essential Job Functions</h4><ul><li>Guiding a large Rails application towards a more mature modular direction, with an increased focus on ease of modification and configurability</li><li>Helping design a clearer vision of the boundaries and patterns driving the future architecture of the app</li><li>Enabling robust integration support for both in-house and 3rd party integrations, helping us get the most out of integrated solutions and offloading anything not core to the business</li><li>Continuing to migrate from REST APIs towards expanding our platform’s next-generation GraphQL APIs, adding functionality and robustness to our growing internal and partner APIs</li><li>Develop processes to improve code quality for growing team, carrying out code reviews as part of the continuous deployment pipeline</li><li>Participate in Pair Programming on a semi-regular basis assisting developers</li><li>Work closely with Product Management and the dev team developing features that add clear value to our users</li><li>Building workflow-based UIs with React for the independent, web-based static front-end client</li><li>Maintaining and incrementally improving a large Application with 5 years of history, both full of powerful historical insights and not entirely understood corners</li><li>Collaborating effectively in a distributed team, using a pragmatic, agile process</li></ul><h4>Ideal Qualifications</h4><ul><li>7+ years’ experience with web backends</li><li>1-year experience with any front-end framework (React, Angular, etc)</li><li>2+ years experience with Rails web framework</li><li>Experience with TDD, Git, Microservices, and Performance Optimizations</li><li>Experience in production debugging, monitoring, and maintenance</li><li>Experience with Distributed Teams</li></ul><h4>What Else We’re Looking For</h4><ul><li><strong>Lifelong Learner. </strong>You are committed to self-improvement, which means you’ve sought and found mentors to learn from. You’re also a natural at mentoring, teaching, and reviewing others’ work, help other engineers make sound architectural decisions, improve their code quality, and get out of their comfort zone. Because you know there’s always more to learn, you don’t get hung up on knowing everything before taking action. As you know so well, taking action is often the only way to discover if something works.</li><li><strong>Technical mastery.</strong> You have worked with and deployed several frameworks and languages. You have favorite frameworks, languages, and methods but enjoy learning and digging into new things. You enjoy working on both smaller and newer applications and large applications with years of operational experience under the hood.</li><li><strong>User-centered orientation.</strong> As much as you love technical challenges for their own sake, you’re inspired by making things that work for real people. You ask tough questions about how requested features solve user problems, and have a great track record of collaborating with designers and product managers on solutions.</li><li><strong>Clear Communication.</strong> You enjoy working as part of a distributed team. You realize that the key to successful remote work is trust, which comes from regular communication through tools like Slack, and respect for the team’s agile process (e.g., Jira stories and GitHub pull requests). You also know the value of face-to-face interaction, even if only through web conferencing tools, and have enjoyed being a great teammate in virtualized contexts.</li><li><strong>Pragmatism</strong>. In a discussion of how to build a feature you will reliably bring people back to the question of what are we trying to achieve, and how will we know when we’re successful? You love new technologies, but will only recommend them if they are the most effective way of solving the problem at hand, and are an expert in playing a devil’s advocate of your own ideas. You are drawn to measure the results of changes to user experiences and look for opportunities to remove features if they are not being used (or used well).</li><li><strong>Lateral thinking</strong>. In software so many of the leaps forward on the non-obvious ones, hence you have a well-honed sense for knowing when to apply oblique methods to a challenging problem that evades straightforward approaches. This may mean being able to connect the dots across different domains (anthropology, economics, psychology, politics), but it always means being willing to suspend judgment through the process of divergence and convergence.</li><li><strong>Curiosity. </strong>Unsatisfied with accepted wisdom, you’ll ask why multiple times when wrapping your head around a new concept or proposal. You’re also fascinated with the kinds of problems your peers in the industry are solving, and the novel approaches they’re taking. This leads you to be involved in local user meetings, online communities, and conferences.</li></ul><h4>Other information</h4><p>You can learn more about us on <a href="https://twitter.com/offgride">Twitter</a>, <a href="https://www.facebook.com/offgridelectric/">Facebook</a> and our <a href="http://zolaelectric.com/">Website</a>. If you are interested in joining our team in either the US (East Coast preferred) or Amsterdam, Netherlands, you can submit your resume via our <a href="https://cezanneondemand.intervieweb.it/offgrid-electric/jobs/senior_ruby_on_rails_engineer_4281/en/">career page</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6e67e03853c0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/help-us-solve-the-developing-worlds-energy-needs-with-software-6e67e03853c0">Help us solve the developing world’s energy needs with software</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why are so many projects late — The science of project management]]></title>
            <link>https://medium.com/oge-tech/why-are-so-many-projects-late-the-science-of-project-management-bd65b32033c7?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/bd65b32033c7</guid>
            <category><![CDATA[planning]]></category>
            <category><![CDATA[science]]></category>
            <category><![CDATA[agile]]></category>
            <category><![CDATA[project-management]]></category>
            <dc:creator><![CDATA[Markus]]></dc:creator>
            <pubDate>Wed, 25 Jul 2018 19:12:10 GMT</pubDate>
            <atom:updated>2018-07-25T19:12:10.354Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ayu5SP1Wn7xBDNT5" /><figcaption>Photo by <a href="https://unsplash.com/@danieleriggifotovideo?utm_source=medium&amp;utm_medium=referral">Daniele Riggi</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Project delays — in many areas they are so common that they don’t even come as a surprise anymore. Think about the last time you worked with a contractor in your house, the last time you participated in a big business initiative or the last time you heard about another big infrastructure project on the news. Delays seem to be everywhere. On the other side, we spend more and more time and effort on ever-changing project planning frameworks and methodologies, training people over their career in everything from PRINCE II (R) to various shades of Agile. Why is there such a disconnect between what we <strong>think</strong> we can achieve (and manage) and the actual time and cost of complex things?</p><p>Daniel Kahneman and Amos Tversky¹ coined the term “Planning Fallacy” in 1979 and later expanded its definition. Today it describes the tendency to underestimate the time, costs, and risks of future actions while overestimating the benefits. The fallacy can be observed in many settings, from one-person tasks (such as the completion of academic papers² ) to the well-chronicled delays in major construction projects, such as the German Airport BER³.</p><p>Kahnemann and Tversky originally explained this fallacy with the planner focusing on the most “optimistic” scenario instead of using their full experience when planning a task or project. While this certainly happens (in my personal experience big companies tend to emphasize “sunny” or “best case” scenarios while planning but forget this emphasis once the predictable delay occurs), even frameworks that specifically account for both known and unknown risks tend to underestimate the total length of a project. Some of the reasons for this are:</p><ul><li><strong>Optimism Bias:</strong> The most obvious explanation for underestimating projects is optimism bias. People tend to be optimistic about their personal exposure to risk (i.e. they underestimate their personal risk⁴) and hence tend to be optimistic about their project’s exposure to the same risks.</li><li><strong>Self-Serving Bias:</strong> Related to the optimism bias, human beings also tend to experience a self-serving bias in their own past performance by taking credit for things that went well but attributing failures to external factors⁵. This leads people to incorrectly recall how long events took in the past and hence to underestimate future projects. It also, together with the optimism bias, leads planners to underestimate the recurrence of events that have delayed a project in the past, such as vacations, illnesses or the time it takes to plan a meeting.</li><li><strong>Focal Bias</strong>: Focal bias describes a bias in which the planner concentrates too much on the part of the problem or project that he knows most about and uses this information to frame the planning decision. As the part the planner knows most about is usually also the safest part of the project (in terms of planning) this leads to underestimation⁶.</li><li><strong>Anchoring and Adjustment:</strong> Anchoring and adjustment describes a mental process for judgments under uncertainty, in which an initial “anchor” value is chosen and adjusted based on the information present⁷. Typically, humans under-adjust their initial anchor in this process, hence the results heavily rely on the chosen anchor. This can even be shown if the anchor is obviously irrelevant or nonsensical⁸. In the context of project planning, the reality in many companies is that projects start with so-called “rough estimates”, which are often little more than wishes. However, those wishes have a heavy influence on the final planning outcome by providing an “anchor” for further updates.</li><li><strong>Motivations:</strong> Counter-intuitively, a highly-motivated and involved project planner is actually counter-productive for planning success. Individuals who are highly motivated to complete the actual tasks (not only the planning of said tasks) in a quick manner underestimate the total duration of the tasks and hence underestimate total project time⁹ . In business, a similar effect often leads to individuals underestimating tasks that they personally enjoy compared to tasks that they find tedious or boring.</li><li><strong>Perceived Power:</strong> When individuals perceive a sense of power, they are especially vulnerable to the planning fallacy¹⁰. The reason for this is the a perception of power tends to narrow the individual’s focus on the principal goals of a project, leading them to disregard peripheral information. This also means that they tend to disregard prior experience and possible obstacles to the project.</li><li><strong>Authorization Imperative: </strong>A business-specific reason for incorrect project planning can be found in the “Authorization Imperative”. This describes the reality in most businesses of requiring authorization (often in the form of an approved “business case”) to start and lead projects. This leads to a strong motivation of the planner to report relatively low project costs, which are usually directly and indirectly tied to project length.</li><li><strong>Focus on opportunities vs obstacles:</strong> An interesting effect was found between focusing on opportunities and and obstacles of a project. Depending on your mental distance to the project (both in time and involvement) focusing on either opportunities or obstacles can make the planning fallacy worse. This is especially true for projects that are mentally close to the planner (e.g. a project that should be started soon and has been worked on for a while). In this case, if the planner is not actively engaged in visualizing obstacles, he tends to disregard them in the final plan¹¹.</li><li><strong>Asymmetry and Scaling:</strong> First popularized by Taleb, asymmetry and scaling refer to the principle that random events are not equally distributed in either impact nor frequency and that negative consequence scale with the complexity of the system¹². Therefore, it is easy to underestimate the negative consequence of random events on a project. This is especially true for complex projects, where positive events usually have a relatively minor impact while the impact of negative random events can essentially be “unlimited”.</li></ul><p>Knowing all of these imperfections of the human mind does not necessarily help us improving our project planning capability. However, there are some strategies that can help generating better plans in the future:</p><ul><li><strong>Segmentation:</strong> Segmentation describes the breaking down of larger tasks into smaller, more manageable pieces. Those pieces are then estimated separately to come up with a total plan. The principle behind Segmentation is that reviewing each piece individually will give more depth to the planning picture and will usually lead to the sum of smaller tasks being more than the estimate on one bigger task¹³. Many modern software packages use segmentation techniques to improve planning and execution of projects.</li><li><strong>Reference Class Forecasting:</strong> A completely different approach to classical project planning is called “Reference Class Forecasting”. This methodology ignores the classic principles of Project Planning such as identifying dependencies and planning discrete tasks and rather identifies “reference projects” that are similar in size and scope and attempts to establish a planning horizon based on those real-life examples. This methodology has been successfully implemented in large government-sized projects¹⁴. Reference Class Forecasting is especially useful to find the correct anchor (see anchoring) for a plan before going too far into detail.</li><li><strong>Impartial Planning:</strong> Given that many of the biases describes above especially apply to individuals that also execute the tasks that were planned, it can be useful to have impartial planners who will not participate in the project itself plan the project or at least parts of it. The classic Project Manager which is not responsible for any part of the project delivery but only its organization is born from this idea. This helps especially if a project has many inter-dependencies and moving parts, as an impartial planner does not have a personal preference (e.g. being especially efficient in his or her own tasks) and will prioritize the overall project timeline above all else.</li></ul><p>An example that combines these approaches into one was an ERP roll-out I was participating in earlier in my career. In this particular case, the total project consisted of many individual implementations in several locations within one company, following a common business process template and using the same solution.</p><p>We were able to increase predictability and precision in planning by using four concrete improvements:</p><ol><li>First, we obtained rough estimates for a single implementation (one location) by talking to the vendor and external companies who had used this software to give us a baseline (reference-class forecasting)</li><li>Then, we used a resource who was experienced in projects of this scope without knowing the specifics of the product to build an overall, high-level roll-out plan (Impartial planning)</li><li>Third, we split up the high-level planning into small pieces to make each step of the way concrete and understandable (segmentation)</li><li>And lastly, we used the learnings from each individual implementation to improve the plan for the following location in the same way</li></ol><p>While our plan still was not perfect, it allowed us to forecast Go-Live dates and a roll-out schedule to a much higher degree than we thought possible at the beginning of the roll-out (we hit all our targets for the first wave of implementations!). Project planning might still be more art than science, but by following what research has shown to work, you can definitely improve your plan!</p><p>Thanks for reading and hopefully the ideas above can help to improve the next project plan or, at least, understand the risks hidden in the neat slides and GANTT charts we are all used to.</p><p><strong>References</strong></p><p>1: Kahneman, Tversky 1979<br>2: Buehler, Griffin, Ross, 1994<br>3: <a href="https://www.telegraph.co.uk/travel/news/berlin-new-airport-delayed-again/">https://www.telegraph.co.uk/travel/news/berlin-new-airport-delayed-again/</a><br>4: O’Sullivan,2015<br>5: Pezzo, Litman et al, 2016<br>6: Buehler and Griffin, 2003<br>7: Kahneman and Tversky, 1974<br>8: Strack and Mussweiler, 1997<br>9: Buehler, Griffin and MacDonald, 1997<br>10: Weick and Guinote, 2010<br>11: Preetz, Buehler, Wilson, 2010<br>12: Taleb, 2007<br>13: Forsynth, 2008<br>14: Flyvbjerg, 2006</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bd65b32033c7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/why-are-so-many-projects-late-the-science-of-project-management-bd65b32033c7">Why are so many projects late — The science of project management</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Off Grid Electric Tech Team Booklist]]></title>
            <link>https://medium.com/oge-tech/offgrid-electric-tech-team-booklist-4a63f85cd2e4?source=rss----483b3493b10f---4</link>
            <guid isPermaLink="false">https://medium.com/p/4a63f85cd2e4</guid>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[technology-management]]></category>
            <category><![CDATA[product-management]]></category>
            <category><![CDATA[project-management]]></category>
            <category><![CDATA[books]]></category>
            <dc:creator><![CDATA[Dan Mayer]]></dc:creator>
            <pubDate>Thu, 05 Apr 2018 18:05:28 GMT</pubDate>
            <atom:updated>2018-04-06T15:25:37.684Z</atom:updated>
            <content:encoded><![CDATA[<p>A topic that often comes up for discussion with peers in the tech community are books that have impacted our thoughts and careers. For the list below I am thinking of more general books that are impactful to our technical careers, but aren’t specific to development, code, or a particular technology. Books that cut across development and discuss how we work as teams, with product management, and incorporating design human thinking into our practices. After several discussions, I thought it might be good to share some of our favorites from the Offgrid Electric team. While this list is always growing, here are some of the most recently talked about books we wanted to share.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Xrfkf3IP0RDMI3wVlYaSCQ.jpeg" /><figcaption>old books from <a href="https://pixabay.com/en/old-books-book-old-library-436498/">pixelbay</a></figcaption></figure><h3><a href="http://amzn.to/29tCPqK">Drive</a></h3><ul><li>author: Daniel Pink</li><li>theme: Behavioral Psychology</li><li>recommended by: <a href="https://medium.com/u/9f96e9614042">Annyce Davis</a></li></ul><p>This book is all about understanding what truly motivates us as humans and how that knowledge can impact the way we approach work and business.</p><h3><a href="https://www.amazon.com/Remote-Office-Required-Jason-Fried-ebook/dp/B00C0ALZ0W/ref=tmm_kin_swatch_0?_encoding=UTF8&amp;qid=1513610470&amp;sr=8-1">Remote: Office Not Required</a></h3><ul><li>authors: Jason Fried &amp; David Heinemeier Hansson</li><li>theme: Distributed Work</li><li>recommended by: <a href="https://medium.com/u/e460595752b3">Moyinoluwa Adeyemi</a></li></ul><p>A great book about how to function as a distributed team. I recommend this to most folks who end up working with a full or partial distributed team. You can read Moyin’s <a href="https://tech.offgrid-electric.com/book-review-remote-office-not-required-e75e19c68529">full review in a previous post</a>.</p><h3><a href="https://www.amazon.com/Deep-Work-Focused-Success-Distracted-ebook/dp/B00X47ZVXM/ref=sr_1_3?s=books&amp;ie=UTF8&amp;qid=1521820071&amp;sr=1-3&amp;keywords=Deep+Work">Deep Work: Rules for Focused Success in a Distracted World</a></h3><ul><li>author: Cal Newport</li><li>theme: Time Management</li><li>recommended by: <a href="https://medium.com/u/3eb55ff5c9cf">Iris van de Kieft</a></li></ul><p>Deep work is the ability to focus without distraction on a cognitively demanding task. It’s a skill that allows you to quickly master complicated information and produce better results in less time. Deep work will make you better at what you do and provide the sense of true fulfillment that comes from craftsmanship.</p><h3><a href="https://www.amazon.com/Antifragile-Things-That-Disorder-Incerto/dp/0812979680">Antifragile</a></h3><ul><li>author: Nassim Taleb</li><li>theme: Systems Design</li><li>recommended by: <a href="https://medium.com/u/5dc7be9aad56">Thor Muller</a></li></ul><p>Taleb challenges how people think about risk, perception and human behavior. Antifragile is possibly his most actionable book for product designers and developers, in that he provides guidance for what any system needs to do if it is to not just withstand shocks and failures, but actually improve when they occur.</p><h3><a href="https://www.amazon.com/Field-Guide-Human-Centered-Design/dp/0991406311/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1521818347&amp;sr=1-1&amp;keywords=human+design+field+guide">The Field Guide to Human-Centered Design</a></h3><ul><li>author: <a href="https://www.amazon.com/s/ref=dp_byline_sr_book_1?ie=UTF8&amp;text=IDEO.org&amp;search-alias=books&amp;field-author=IDEO.org&amp;sort=relevancerank">IDEO.org</a></li><li>theme: Human Centered Design</li><li>recommended by: <a href="https://medium.com/u/3736ca727f9b">Nick Stevens</a></li></ul><p>A step-by-step guide that will get you solving problems like a designer.</p><h3><a href="https://www.amazon.com/Mythical-Man-Month-Software-Engineering-Anniversary-ebook/dp/B00B8USS14/ref=mt_kindle?_encoding=UTF8&amp;me=">The Mythical Man-Month: Essays on Software Engineering</a></h3><ul><li>author: Frederick P. Brooks Jr.</li><li>theme: Software Project Management</li><li>recommended by: <a href="https://medium.com/u/6538421f98dc">Dan Mayer</a></li></ul><p>Assigned to me in College, one of the tech books I still think about most often. With a blend of software engineering facts and thought-provoking opinions, Fred Brooks offers insight for anyone managing complex projects.</p><h3>Looking for More?</h3><p>Below I will list the rest of the books on our team’s list without all the details.</p><ul><li><a href="https://www.amazon.com/Radical-Candor-Kim-Scott/dp/B01KTIEFEE/ref=sr_1_4?s=books&amp;ie=UTF8&amp;qid=1521819436&amp;sr=1-4&amp;keywords=radical+candor">Radical Candor: Be a Kick-Ass Boss Without Losing Your Humanity</a></li><li><a href="https://www.amazon.com/Emotional-Intelligence-2-0-Travis-Bradberry-ebook/dp/B002U3CBUW/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1521818491&amp;sr=1-1-spons&amp;keywords=hard+thing+about+hard+things">Emotional Intelligence 2.0</a>, thx <a href="https://medium.com/u/7ae1a5eb24ad">Dian Rosanti</a></li><li><a href="https://www.amazon.com/Creativity-Inc-Overcoming-Unseen-Inspiration/dp/0812993012/ref=sr_1_1?ie=UTF8&amp;qid=1521818286&amp;sr=8-1&amp;keywords=creativity+inc">Creativity, Inc</a></li><li><a href="https://www.amazon.com/Five-Dysfunctions-Team-Leadership-Fable/dp/0787960756/ref=sr_1_3?s=books&amp;ie=UTF8&amp;qid=1521819337&amp;sr=1-3&amp;keywords=+5+dysfunctions+of+a+team">The Five Dysfunctions of a Team: A Leadership Fable</a></li><li><a href="https://www.amazon.com/Advantage-Enhanced-Organizational-Everything-Business-ebook/dp/B006ORWT3Y/ref=sr_1_4?s=books&amp;ie=UTF8&amp;qid=1521820959&amp;sr=1-4&amp;keywords=the+advantage">The Advantage, Enhanced Edition: Why Organizational Health Trumps Everything Else In Business</a></li><li><a href="https://www.amazon.com/Checklist-Manifesto-How-Things-Right/dp/0312430000">The Checklist Manifesto</a>, thx <a href="https://medium.com/u/7ff7e3575cc">Galt Elijah MacDermot</a></li><li><a href="https://www.amazon.com/Managers-Path-Leaders-Navigating-Growth-ebook/dp/B06XP3GJ7F/ref=sr_1_4?s=books&amp;ie=UTF8&amp;qid=1521819848&amp;sr=1-4&amp;keywords=manager%27s+path">The Manager’s Path: A Guide for Tech Leaders Navigating Growth and Change</a></li><li><a href="https://www.amazon.com/High-Output-Management-Andrew-Grove-ebook/dp/B015VACHOK/ref=sr_1_4?s=books&amp;ie=UTF8&amp;qid=1521820526&amp;sr=1-4&amp;keywords=high+output+management">High Output Management</a></li><li><a href="https://www.amazon.com/Effective-Executive-Definitive-Harperbusiness-Essentials/dp/0060833459/ref=sr_1_1?ie=UTF8&amp;qid=1522005038&amp;sr=8-1&amp;keywords=effective+Executive&amp;dpID=51XhIvdXK0L&amp;preST=_SY291_BO1,204,203,200_QL40_&amp;dpSrc=srch">The Effective Executive: The Definitive Guide to Getting the Right Things Done</a></li><li><a href="https://www.amazon.com/dp/B00ACC5Z1U/?coliid=I1TL6OYCMBUB8J&amp;colid=NT2OSIG2COH&amp;psc=0&amp;ref_=lv_ov_lig_dp_it">Managing Humans: Biting and Humorous Tales of a Software Engineering Manager</a></li><li><a href="https://www.amazon.com/dp/B00DY5A8X2/?coliid=I3N4K9241Z9FX1&amp;colid=NT2OSIG2COH&amp;psc=0&amp;ref_=lv_ov_lig_dp_it">Peopleware: Productive Projects and Teams</a></li><li><a href="https://www.amazon.com/First-Days-Updated-Expanded-Strategies-ebook/dp/B00B6U63ZE">The First 90 Days, Updated and Expanded: Proven Strategies for Getting Up to Speed Faster and Smarter</a></li><li><a href="https://www.amazon.com/Hard-Thing-About-Things-Building-ebook/dp/B00DQ845EA/ref=sr_1_4?s=books&amp;ie=UTF8&amp;qid=1521818491&amp;sr=1-4&amp;keywords=hard+thing+about+hard+things">The Hard Thing About Hard Things</a></li><li><a href="https://www.amazon.com/Slack-Getting-Burnout-Busywork-Efficiency-ebook/dp/B004SOVC2Y/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1521818649&amp;sr=1-1&amp;keywords=slack">Slack: Getting Past Burnout, Busywork, and the Myth of Total Efficiency</a></li><li><a href="https://www.amazon.com/Righteous-Mind-Divided-Politics-Religion/dp/0307455777/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1521823478&amp;sr=1-1&amp;keywords=the+righteous+mind">The Righteous Mind</a></li><li><a href="https://www.amazon.com/Thinking-Fast-Slow-Daniel-Kahneman/dp/0374533555/ref=sr_1_3?s=books&amp;ie=UTF8&amp;qid=1521823603&amp;sr=1-3&amp;keywords=thinking+fast+and+slow">Thinking, Fast and Slow</a></li><li><a href="https://www.amazon.com/Art-Thinking-Clearly-Rolf-Dobelli-ebook/dp/B00IZP6EVQ/ref=sr_1_3?s=digital-text&amp;ie=UTF8&amp;qid=1522861282&amp;sr=1-3&amp;keywords=The+Art+of+Thinking+Clearly">The Art of Thinking Clearly</a></li><li><a href="https://www.amazon.com/Manage-Your-Day-Day-Creative/dp/1477800670/ref=sr_1_fkmr2_1?ie=UTF8&amp;qid=1522949910&amp;sr=8-1-fkmr2&amp;keywords=manage+your+day+today+99u">Manage Your Day-to-Day: Build Your Routine, Find Your Focus, and Sharpen Your Creative Mind</a></li><li><a href="https://www.amazon.com/Sprint-Solve-Problems-Test-Ideas/dp/150112174X/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1522950028&amp;sr=1-1&amp;keywords=sprint">Sprint: How to Solve Big Problems and Test New Ideas in Just Five Days</a></li></ul><p>If you want an even more tech management recommendations, I found this post, the <a href="https://blog.chaddickerson.com/2018/01/09/best-books-for-new-first-time-managers/">Best books for new, first-time managers</a>, to be inspiring as well.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4a63f85cd2e4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/oge-tech/offgrid-electric-tech-team-booklist-4a63f85cd2e4">Off Grid Electric Tech Team Booklist</a> was originally published in <a href="https://medium.com/oge-tech">Power the People</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>