<?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: Maciej</title>
    <description>The latest articles on DEV Community by Maciej (@malak).</description>
    <link>https://dev.to/malak</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%2F915807%2Fb17d10b4-c247-4d8e-9b97-4e0cf39c61db.jpeg</url>
      <title>DEV Community: Maciej</title>
      <link>https://dev.to/malak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/malak"/>
    <language>en</language>
    <item>
      <title>You shouldn’t take AWS Pearson Vue Online Proctoring exam and here is why</title>
      <dc:creator>Maciej</dc:creator>
      <pubDate>Tue, 13 Dec 2022 10:54:54 +0000</pubDate>
      <link>https://dev.to/malak/you-shouldnt-take-aws-pearson-vue-online-proctoring-exam-and-here-is-why-3a0p</link>
      <guid>https://dev.to/malak/you-shouldnt-take-aws-pearson-vue-online-proctoring-exam-and-here-is-why-3a0p</guid>
      <description>&lt;p&gt;Some time ago I took AWS Architect Associate exam. I’m already a holder of a AWS Developer Associate certification so I expected no issues. I couldn’t be more wrong!&lt;/p&gt;

&lt;p&gt;I will tell you what to expect, what the policies are, what surprised me and why you should avoid online proctoring exam, if you have alternatives.&lt;/p&gt;

&lt;p&gt;What you will be obliged to do before even starting an exam?&lt;br&gt;
Be sure to run system test. You should do it in the same room, with the same devices and WiFi that you will use during an exam. I have couple of WiFi networks which can be switched dynamically so I checked all of the options beforehand. Think what could go wrong because something most likely will.&lt;br&gt;
Read the online exam policy, that’s the best one thing you can do. And don’t be scared of it at all. I won’t elaborate on it. I will describe what wasn’t there later in this article so you will be well-prepared!&lt;br&gt;
Disconnect all external devices from your PC/Mac/laptop. Remove them from the desk. That includes external monitor if you’re using MacBook or Windows powered laptop. Any Linux powered devices are strictly prohibited. I ended up disconnecting and removing my monitor 10 minutes before an exam! My fault.&lt;br&gt;
You have to be in a closed, well-lit and calm environment. They need to see closed doors as a part of “room verification” process. No one can enter, no one can talk, remember!&lt;br&gt;
What was surprising since it wasn’t written in the policy?&lt;br&gt;
The recommendation is to start 30 minutes (!) before exam scheduled time. But it isn’t written why. It’s because entire verification process takes eons. In my case it took 1h. Sitting and waiting was the major part of it. Although I saw it could be worst than that. One must be ready to spend 140 minutes of exam alone. Additionally, there will be couple of minutes on pre- and post-exam activities. Finally, you would have to spend from (promised) 15 minutes to 1h (or more as few people shared) on verification process. That adds up to almost 4h spent in one position in front of your computer. Remember, while verification process and exam is happening you cannot stand up, you cannot go to bathroom, you cannot drink nor eat, you cannot talk and you cannot look into window. Talk to your partner and/or roomies and plan for it. You have to make your room a Temple Of Silence for a time being.&lt;/p&gt;

&lt;p&gt;As I mentioned, they will verify your identity. That’s understandable and would also happen on-site. The verification process can be done via your smartphone, for example. Also, a proctor might try resolve technical issues with an exam by calling your phone. But, according to policy, you’re not allowed to have phone within your reach. Nor any smart devices. If someone would call you — exam could be cancelled. I recommend having your phone on silent mode, at least 1,5h meters away but with visible screen — so you know when agent is calling (strange, unknown and international number) and you won’t end up with cancelled exam because you picked up a call from your boss/partner/parent/friend.&lt;/p&gt;

&lt;p&gt;Policy also states that you need government issued ID card in order to identify yourself before an exam. During the verification process I was able to choose from driver license or passport only. ID card is not an option and that was a big surprise to me. I had to find one of the approved documents very quickly. Don’t do it to yourself and prepare ID card and the second document beforehand, just to be sure.&lt;/p&gt;

&lt;p&gt;So, what really happened and why I’m simply disappointed?&lt;br&gt;
As a part of verification process I was contacted by a polite agent.&lt;br&gt;
She checked my workspace using my camera view, so, what’s behind&lt;br&gt;
and in front of my desk, my hands and wrists for watches and cheat sheets.&lt;br&gt;
All good! Good to go!&lt;/p&gt;

&lt;p&gt;Everything was fine until she disconnected from the chat and remotely triggered an exam. My entire screen went black instantly.&lt;br&gt;
The only thing I was able to see was an overflow menu with the following items: Pearson logo, chat, whiteboard and my camera view.&lt;br&gt;
I waited around 10 minutes hoping for exam to be loaded. Then, I clicked ‘chat’ icon — instantly — entire application froze. I waited for some time hoping it will ‘unfroze’, unfortunately, it have not happened. I had to force restart entire device.&lt;/p&gt;

&lt;p&gt;I’m running MacBook Pro with quad-core CPU and 16GB of RAM. You can be sure it’s enough. I’m running Android Studio, Docker and much more on a daily basis. Additionally, I was at 300Mbs fiber backed internet connection. So, I kinda blame Pearson software.&lt;/p&gt;

&lt;p&gt;How to resolve broken exam situation?&lt;br&gt;
You need to go to AWS dedicated Pearson help center. Since my country don’t have dedicated Pearson phone number I spent more than 1 hour waiting for online assistant. It took around 15 minutes to open a case. Now, I need to wait from 3 to 5 business days to resolve the situation. The entire process is “very corporate” if you understand what I mean. It’s slow.&lt;/p&gt;

&lt;p&gt;Take exam on site!&lt;br&gt;
Take the above mentioned points into consideration while scheduling an exam. It’s all understandable but it’s quite limiting comparing to taking an exam on site. On site you can go to bathroom, drink, you will have a piece of paper to work on, you can relax a bit. If you’re able to schedule on site I recommend it. I won’t do online exam again unless #covid19 will force me to.&lt;/p&gt;

&lt;p&gt;Obviously, shit happens. I’m not furious nor mad but I want to warn you what may happen, how reality looks like, so you’re not disappointed. Every student spends a lot of time beforehand in order to pass an exam — it’s a bit frustrating when technical issues gets in the way.&lt;br&gt;
I understand software sometimes is not functioning as expected as I’m also introducing issues from time to time. All software engineers do.&lt;br&gt;
Simply, if I would have a choice between on site stable exam environment and unstable, unpredictable software — my choice is obvious.&lt;/p&gt;

&lt;p&gt;What was your experience with online proctor exam? Let others know in the comments!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>marketing</category>
    </item>
    <item>
      <title>How to start writing reusable components for Android apps?</title>
      <dc:creator>Maciej</dc:creator>
      <pubDate>Sat, 27 Aug 2022 19:03:00 +0000</pubDate>
      <link>https://dev.to/malak/how-to-start-writing-reusable-components-for-android-apps-4n90</link>
      <guid>https://dev.to/malak/how-to-start-writing-reusable-components-for-android-apps-4n90</guid>
      <description>&lt;p&gt;How to start writing reusable components for Android apps?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The purpose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For anyone, like myself, interested in building custom, reusable view components for an Android app. And those who had problems with finding good guidelines on the topic.&lt;/p&gt;

&lt;p&gt;I will provide the basic reasoning behind architecting custom reusable views. There won’t be implementation details (code) for many reasons, &lt;br&gt;
one of those is my trial to focus on the &lt;em&gt;concept&lt;/em&gt; rather than technicalities.&lt;/p&gt;

&lt;p&gt;Anyway, I hope that after reading this article you will be able to apply those principles to any interface architecture pattern such as MVP, MVC, MVVM &lt;br&gt;
or MVI.&lt;/p&gt;

&lt;p&gt;The first part says &lt;em&gt;why and when&lt;/em&gt; adapt such technique. &lt;br&gt;
You can jump to the second part for the list of recommendations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, ask yourself a question: Do we need reusable components?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;IMHO you should be a part of a bigger, long-lasting project to consider doing so. I understand it’s hard to draw a line between MVP/short-term &lt;br&gt;
and a long-term project sometimes. It is sometimes also hard to let go of technical nicety such as custom views but please be mature to do so. &lt;br&gt;
The following list should help you making a decision.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H-Okh1g6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2A8l4Mdcs-At2Q-PM-aJBcbA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H-Okh1g6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2A8l4Mdcs-At2Q-PM-aJBcbA.jpeg" alt="Definitely a hard nut to crack. via [meme generator](https://imgflip.com/memegenerator)" width="577" height="433"&gt;&lt;/a&gt;&lt;em&gt;Definitely a hard nut to crack. via &lt;a href="https://imgflip.com/memegenerator"&gt;meme generator&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;YAYS&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you’re in a flexible environment where design decisions are done based upon &lt;strong&gt;many perspectives&lt;/strong&gt;(not just your own) so you can pitch for good UX/UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you see one (or more) view is used (will be used) in many places (reused). Plan for it. Those views should have &lt;em&gt;fairly&lt;/em&gt; similar UX/UI to serve as a reusable components. Otherwise, reusing them will be challenging. Again, pitch the team to sustain consistent user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If your teams plans to build extremely custom view like karaoke, &lt;br&gt;
piano keyboard, fancy animated overlays, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, I have not tested this theory but IMHO if you’re bored and you feel like making an old legacy project interesting. &lt;br&gt;
Since writing reusable components is like writing complex new features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you’re able to deliver business value by creating such components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If your team plans major refactors and you have time to just play with the product.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOPE&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;POC or MVP cases. Although, MVP cases could be a good playground.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Small, simple app cases. There are situations where you know an app will be coded once and left as is. BTW Consider going cross-platform with those. Again, nice playground.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An inflexible environment. Design system gave to you up-front instead &lt;br&gt;
of being discussed with all parties involved.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your codebase is not ready to handle reusable components. &lt;br&gt;
Please see &lt;strong&gt;Prerequisites&lt;/strong&gt; *&lt;em&gt;section at the very end for few tips. &lt;br&gt;
In this case you might have to put some work beforehand in order to *enable&lt;/em&gt; reusable components.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8QvPhq8f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AyuyQNA5y2y7LhGBidiY9IQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8QvPhq8f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AyuyQNA5y2y7LhGBidiY9IQ.jpeg" alt="Brace yourself! via [meme generator](https://imgflip.com/memegenerator)" width="651" height="383"&gt;&lt;/a&gt;&lt;em&gt;Brace yourself! via &lt;a href="https://imgflip.com/memegenerator"&gt;meme generator&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ok, so now you’re &lt;strong&gt;sure&lt;/strong&gt;. Let’s go with recommendations!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Custom views&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It could be quite obvious for some and not necessary for some others &lt;br&gt;
as I noticed. Deeply research the case of custom views first and try to imagine them as reusable and self-contained components. Don’t be afraid. &lt;br&gt;
There are &lt;a href="https://developer.android.com/guide/topics/ui/custom-components"&gt;plenty of materials&lt;/a&gt; to learn how to create simple custom view. &lt;br&gt;
Remember, it doesn’t mean you’re responsible for rendering everything yourself. It doesn’t mean you have to create those components from scratch neither. It will be the better and simpler version of what Fragments suppose to be.&lt;/p&gt;

&lt;p&gt;Think of a custom view as a container (f.ex ViewGroup like FrameLayout) &lt;br&gt;
for other components grouped together to &lt;strong&gt;serve one purpose&lt;/strong&gt;. &lt;br&gt;
For example list of results used in couple places, part of product detail screen, or maybe just a grid product item used on different screens of your app, &lt;br&gt;
or a search-bar and corresponding list of results. In the Toolbar/bars case &lt;br&gt;
be careful. We had a lot of work with extracting Toolbar out of custom view after we realised Toolbar should be a separate, reusable component itself. &lt;br&gt;
The important parts are &lt;em&gt;reusability&lt;/em&gt;, &lt;em&gt;testability&lt;/em&gt; and &lt;em&gt;independency&lt;/em&gt;. &lt;br&gt;
Decouple your view and controller from everything else, more on this shortly.&lt;/p&gt;

&lt;p&gt;I would suggest to read about &lt;a href="https://bradfrost.com/blog/post/atomic-web-design/"&gt;Atomic Design&lt;/a&gt; in order to imagine the building process better. Basically, think of an any widget like button or label as a basic building block for bigger component. No matter if custom or native one. &lt;br&gt;
Then imagine your custom, reusable view (imagine now list of results with reload button) and just compose those two views together. Handle composed views through your view controller or/and communicate through business logic (f.ex in a reactive way but that is not an only option) and you’re good to go.&lt;/p&gt;

&lt;p&gt;Please remember, it is complex stuff. Probably the first place you will choose for refactor will be the wrong one. It was the case in my case. &lt;br&gt;
My tip would be to go with the simplest view first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;DON’TS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Fragments inside “main” fragment.&lt;br&gt;
&lt;a href="http://hannesdorfmann.com/android/mosby3-mvi-4"&gt;Parent-child&lt;/a&gt; relation between views or view controllers. &lt;br&gt;
Toolbar as a part of custom view. &lt;br&gt;
Navigation bottom bar (iOS Toolbar) as a part of custom view. &lt;br&gt;
Coupling. Activity/Fragments does setup for view.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;DOS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Custom view that &lt;strong&gt;serves one purpose&lt;/strong&gt;. &lt;br&gt;
Activity/Fragments just render/show/hide view.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use platform-native components as much as possible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Speaking of custom views, don’t go fully custom if not forced to. &lt;br&gt;
Reusing existing components and widgets just makes your life easier &lt;br&gt;
and enables you to work on &lt;strong&gt;actual, unique to your app business value&lt;/strong&gt;. Work alongside with product/UX/UI/your own team to build &lt;em&gt;native-first&lt;/em&gt; design systems. In most cases there is no need to go completely custom &lt;br&gt;
and I’m sure your client is more than happy to get rid of an animation or fancy screen transition just to &lt;strong&gt;get business value on the place&lt;/strong&gt;. &lt;br&gt;
Ours clients are #trueStory.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What do you mean by native components?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Google did a great job introducing &lt;a href="https://material.io/develop/android/"&gt;Material Components&lt;/a&gt; — guidelines with the corresponding implementation you can reuse in your application.&lt;/p&gt;

&lt;p&gt;You could also consider not-views related components like &lt;a href="https://developer.android.com/guide/navigation/navigation-getting-started"&gt;Navigation Component&lt;/a&gt; which plays nicely with bottom nav bar f.ex. and could simplify your codebase.&lt;/p&gt;

&lt;p&gt;You should take a look on how to compose and reuse themes and styles for your application, &lt;a href="https://www.youtube.com/watch?v=6o3KqyX_tEA&amp;amp;feature=youtu.be"&gt;here is a presentation from Googlers on the topic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But in the end just reuse old, good widgets and layouts to get things done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use modern app layer (view layer) designs patterns&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yep, and not only for the app layer. Plenty articles on that matter. I recommend you to read &lt;a href="http://hannesdorfmann.com/android/mosby3-mvi-1"&gt;Hannes Dorfmann’s story on MVI&lt;/a&gt; since a lot of useful concepts and ideas are there. He elaborates on topics such as how to organise, test, reuse and maintain your code which is not a part of this article but those two topics are strictly related. On the other hand I’m not a fan of tools like &lt;a href="https://github.com/sockeqwe/mosby"&gt;Mosby&lt;/a&gt; or &lt;a href="https://github.com/moxy-community/Moxy"&gt;Moxy&lt;/a&gt; because I painfully found out they are rather constraining. Inheriting behaviour, state or plugins from base classes was catastrophic in our cases. Refactors and building custom behaviour for new screens were messy tasks and required much effort. Instead, use lifecycle aware components (again, see Android tools) or setup your architecture the way that you can call your business logic anytime. Some people calls it overhead. I would call it simplicity especially most of us happen to use caching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Use dependency inversion frameworks extensively&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nowadays, there is world beyond &lt;a href="https://dagger.dev/android"&gt;Dagger&lt;/a&gt;. Dagger itself offers quite good support for Android apps also. You can leverage &lt;a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle"&gt;dependency inversion principle&lt;/a&gt; alongside with framework of your choosing to inject views the same way you are injecting other collaborators like controllers, use cases or data sources. That way you’re decoupling actual view from the place it is rendered.&lt;/p&gt;

&lt;p&gt;The great news is your custom view is &lt;a href="https://developer.android.com/reference/android/view/View"&gt;View&lt;/a&gt; implementation which means your Fragment or Activity doesn’t have to know what is rendered inside. Providing View subclasses enables you to use many custom views &lt;strong&gt;interchangeable&lt;/strong&gt; as particular a component. Voila! Basic A/B testing setup done. Based on condition X you’re providing View Y or Z. Or you want to support multiple apps with one codebase. No worries, just inject different views for different flavours. There is much flexibility in this approach.&lt;/p&gt;

&lt;p&gt;The only thing that Fragment or Activity need to do is to add view &lt;br&gt;
to the layout (programmatically or through inflated xml if you want to &lt;br&gt;
be direct). Don’t over-engineer it since you cannot escape from those &lt;br&gt;
few lines. Just show/hide/gone view first based on the state that must &lt;br&gt;
be rendered on the screen, and then optimise if necessary. &lt;br&gt;
We’re building an app for Android OS 6+ so obviously those devices are quite fast devices. We have not encountered any glitches.&lt;/p&gt;

&lt;p&gt;Oh, and really focus on cleaning up dependencies declarations like Dagger Modules. If you don’t understand it — spend time on it. Leave readable &lt;br&gt;
and understandable legacy to you colleagues. Piling up dependencies are the worst enemy of a clean dependency graph, could lower performance and definitely causes headaches while coding the solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Infamous parent-child dependency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don’t introduce &lt;a href="http://hannesdorfmann.com/android/mosby3-mvi-4"&gt;parent-child dependency&lt;/a&gt; in case of custom views. They really need to be testable and independent. You don’t want to create/mock parent view or controller in order to test a child. And you would need to create or mock such instances for UI or unit test eventually. Don’t introduce hidden dependencies like setup of a custom view partially delegated to the Activity/Fragment or parent view. And definitely don’t inject your Activity/Fragment to a custom view which could lead to cycle dependency and memory leaks. That would also mean other developers will not understand how to use your component. It’s not in reusable fashion.&lt;/p&gt;

&lt;p&gt;The same goes for view controllers (Presenters, ViewModels, Models, etc). Don’t introduce parent-child link between them. Navigation and analytics are the common mistake — just inject those (hopefully) reusable components to custom view controller instead of calling methods from parent controller in order to do any job like analytics tracking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In summary, what was the effect of aligning to those principles…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Simply put, reusable components which could be used anywhere in the app with only small changes to the codebase required. By architecting those properly you should be able to decouple from Android lifecycle hell. &lt;br&gt;
TBH we still sometimes need to sync data &lt;em&gt;onResume()&lt;/em&gt; or &lt;em&gt;onPause()&lt;/em&gt; somehow. That is why we created an abstract class &lt;em&gt;CustomView&lt;/em&gt; with &lt;em&gt;onForeground()&lt;/em&gt; &lt;br&gt;
and &lt;em&gt;onBackground()&lt;/em&gt; methods which are then implemented by custom views. We have done it for simplify, since it’s not breaking good architecture rules. We’re still doing our research on this topic. We know some tricks including lifecycle aware components but in the end our goal was to decouple from lifecycle! I will update this article when and if solved.&lt;/p&gt;

&lt;p&gt;So, for example, by creating a list of results which was decoupled completely from other components we were able to switch views inside the screen in couple of lines enabling &lt;strong&gt;A/B testing&lt;/strong&gt; and easy multiple &lt;strong&gt;flavour&lt;/strong&gt; handling. It is also in the testing stage, so stay tuned. I’m definitely eager to edit this or write new article after long-term usage of the solution.&lt;/p&gt;

&lt;p&gt;The way to achieve that wasn’t easy. We talked to the client and other devs &lt;br&gt;
a lot in order to simplify and learn what is really needed. &lt;br&gt;
We started refactor in one place and then thrown away code. And started again because system and requirements were quite complex. But that enabled us to understand business requirements and technical dependencies which opened new perspective on the solution. We knew how to simplify.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I assumed your codebase is prepared for custom views in a way that many other collaborators are already reusable, testable, independent components. Or at least you’re able to inject them where needed. That would mean Analytics, Navigators, business logic, sensor handlers components and much more. That really depends on your application and you need to resolve it for yourself. Hopefully, by using the above mentioned principles as a reference. Although, there are few basic rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Your business logic is separated from views and view controllers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your business logic can be easily injected and reused.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;View controllers are decoupled from views and how views are rendered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;View controllers are &lt;em&gt;framework agnostic&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;View controllers are easily injectable to views.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Views are are easily injectable to Fragments/Activities.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DISCLAIMER&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is my story behind architecting reusable components for an Android app. I’m not saying it is silver bullet nor the best practise. Still, I consider above rules as a great approach to organise our system. &lt;br&gt;
It was &lt;strong&gt;the first iteration&lt;/strong&gt; we did in &lt;a href="https://tigerspike.com/"&gt;Tigerspike&lt;/a&gt; as a part of the larger project so stay tuned for more! Big shout out to my colleagues as they made this article possible. :)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I would love to see your comments, ideas and improvements as I’m looking to improve and share knowledge as much as possible. &lt;br&gt;
If you want to reach me out I’m based in Wrocław, Poland. &lt;br&gt;
From time to time I’m visiting London. &lt;br&gt;
Here is my &lt;a href="https://pl.linkedin.com/in/maciejmalak"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://twitter.com/monkeydevspl"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>android</category>
      <category>mobile</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>App Links: Associating Multiple Android App Build Types with a Single Domain</title>
      <dc:creator>Maciej</dc:creator>
      <pubDate>Sat, 27 Aug 2022 18:58:00 +0000</pubDate>
      <link>https://dev.to/malak/app-links-associating-multiple-android-app-build-types-with-a-single-domain-7mc</link>
      <guid>https://dev.to/malak/app-links-associating-multiple-android-app-build-types-with-a-single-domain-7mc</guid>
      <description>&lt;p&gt;Although I won’t go into the configuration nor benefits of Android App Links, &lt;br&gt;
you can find them here, here, and here, I would like to elaborate on the missing documentation bit.&lt;/p&gt;

&lt;p&gt;Specifically, how to associate and verify ownership between your domain &lt;br&gt;
(a website) and multiple build types &lt;em&gt;of the same&lt;/em&gt; Android application&lt;br&gt;
using a Digital Asset Links JSON file. &lt;br&gt;
Simply put, how to get rid of disambiguous dialog in such an environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2MgWEZax--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AsNCApKUp4caWwqZWqXZrMA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2MgWEZax--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AsNCApKUp4caWwqZWqXZrMA.jpeg" alt="We do not want that, ever! Source: [https://www.flickr.com/photos/adewale_oshineye/21830113332](https://www.flickr.com/photos/adewale_oshineye/21830113332) Licence CC." width="576" height="1024"&gt;&lt;/a&gt;&lt;em&gt;We do not want that, ever! Source: &lt;a href="https://www.flickr.com/photos/adewale_oshineye/21830113332"&gt;https://www.flickr.com/photos/adewale_oshineye/21830113332&lt;/a&gt; Licence CC.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While it’s not groundbreaking research, this guide can spare you &lt;br&gt;
a couple of hours or even days of waiting impatiently to see if ownership &lt;br&gt;
over an app has been verified.&lt;/p&gt;

&lt;p&gt;In most cases, you’re not able to deploy a Digital Asset Links JSON file &lt;br&gt;
on your own, thus you’re dependent on a third party to do so. &lt;br&gt;
It means waiting. It’s better to do it once, properly.&lt;/p&gt;

&lt;p&gt;App links (deep links) are usually not a standalone functionality of an app &lt;br&gt;
yet rather a collaboration between many teams that drives users &lt;br&gt;
into an app (for example, marketing campaigns).&lt;/p&gt;

&lt;p&gt;More precisely app links usually require a bit of collaboration from a mobile team and marketing/DevOps/web/backend/you-name-it team from your company.&lt;/p&gt;

&lt;p&gt;Let’s give an answer by starting with a question.&lt;/p&gt;

&lt;p&gt;Given the fact that an app has multiple build types, each having its own, distinct certificate fingerprint SHA-1&lt;br&gt;
When a subset of build types points into the same domain&lt;br&gt;
Then could ownership be verified for all build types using a single domain?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r4Kv2O9P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AMSFfUt4ondPMJ5aVmTPGuQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r4Kv2O9P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AMSFfUt4ondPMJ5aVmTPGuQ.jpeg" alt="This is the “complexity” we’re facing. Source: DIY." width="742" height="692"&gt;&lt;/a&gt;&lt;em&gt;This is the “complexity” we’re facing. Source: DIY.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s give an example since it might be confusing.&lt;/p&gt;

&lt;p&gt;An app has DEV (&lt;em&gt;debug&lt;/em&gt;) and QA/UAT/SIT (&lt;em&gt;debug&lt;/em&gt; and &lt;em&gt;release&lt;/em&gt;) build type configurations that are both pointing to the same domain (website), &lt;br&gt;
say, &lt;em&gt;uat.example.com&lt;/em&gt;. That means you have two “apps” with &lt;br&gt;
two distinct signing key configs: two distinct SHA-1 certificate fingerprints are being used to validate ownership of App Links via a single domain that hosts the assetlinks.json file.&lt;/p&gt;

&lt;p&gt;If you have a single domain, can you verify ownership of two apps using two distinct certificates?&lt;/p&gt;

&lt;p&gt;Yes, you can, it’s &lt;a href="https://developer.android.com/training/app-links/verify-site-associations#multiple-apps"&gt;documented&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our case is a bit different. What if an app also has links configured for &lt;em&gt;uat.mobile.example.com&lt;/em&gt; subdomain and both &lt;em&gt;debug&lt;/em&gt; and &lt;em&gt;release&lt;/em&gt; build types of the same app are pointed into it? Could I verify ownership upon a second subdomain using both &lt;em&gt;debug&lt;/em&gt; and &lt;em&gt;release&lt;/em&gt; certificates again?&lt;/p&gt;

&lt;p&gt;The answer is: Yes, it can be easily achieved.&lt;/p&gt;

&lt;p&gt;This is an assetlinks.json content you have to &lt;a href="https://developer.android.com/training/app-links/verify-site-associations"&gt;upload&lt;/a&gt; to your subdomains.&lt;/p&gt;

&lt;p&gt;In the example above, the Digital Asset Links JSON file is configured to support a particular build type&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“package_name”: “com.example.app.buildType1”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;which should be verified using debug SHA-1 certificate&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“sha256_cert_fingerprints”: [“debugCertificateFingerprintHere”]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;while the second build type has a “release” nature and uses a release SHA-1 certificate for ownership to be verified&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“package_name”: “com.example.app.buildType2”,&lt;br&gt;
“sha256_cert_fingerprints”: [“releaseCertificateFingerprintHere”]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lemme explain a bit. The Digital Asset Links JSON file is a plain JSON file.&lt;/p&gt;

&lt;p&gt;It contains a list (table) of elements as a top-level element.&lt;/p&gt;

&lt;p&gt;All you have to do is to provide an entry for each of your build types with an appropriate SHA-1 fingerprint. And &lt;a href="https://developer.android.com/training/app-links/verify-site-associations"&gt;deploy&lt;/a&gt; it in &lt;a href="https://developers.google.com/digital-asset-links/v1/create-statement#website_statement_file"&gt;each domain&lt;/a&gt; you’re interested in. Voila!&lt;/p&gt;

&lt;p&gt;This configuration is extremely useful if you love the flexibility and the opportunity of using any number of environments with any of your build types. It enhances the testability of an app and increases data diversity while easing debugging— all because you can choose which environment will be used while implementing and testing your application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--237bVnyG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2098/1%2Apqei5mOJjnLprLAotZuAZg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--237bVnyG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2098/1%2Apqei5mOJjnLprLAotZuAZg.jpeg" alt="Yeah, **sharing is caring**, remember that!" width="880" height="1007"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Yeah,&lt;/em&gt;&lt;em&gt;sharing is caring&lt;/em&gt;&lt;em&gt;, remember that!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Thank you, &lt;a href="https://www.linkedin.com/in/wdawiskiba/"&gt;Wojtek&lt;/a&gt; for the peer review. It’s delightful to have such support. :)&lt;/p&gt;

&lt;p&gt;If you wanna reach me do not hesitate to do so. :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/maciejmalak/"&gt;Linkedin&lt;/a&gt; | &lt;a href="https://twitter.com/MonkeyDevsPl"&gt;Twitter&lt;/a&gt; | &lt;a href="http://hellomonkeydevs@gmail.com"&gt;email&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>mobile</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Be effective with Bitrise CI for Android — the lessons I learned the hard way.</title>
      <dc:creator>Maciej</dc:creator>
      <pubDate>Fri, 26 Aug 2022 09:52:43 +0000</pubDate>
      <link>https://dev.to/malak/be-effective-with-bitrise-ci-for-android-the-lessons-i-learned-the-hard-way-4hlj</link>
      <guid>https://dev.to/malak/be-effective-with-bitrise-ci-for-android-the-lessons-i-learned-the-hard-way-4hlj</guid>
      <description>&lt;h1&gt;
  
  
  Be effective with Bitrise CI for Android — lessons I learned the hard way.
&lt;/h1&gt;

&lt;p&gt;I won’t elaborate here on how important and crucial for any software development-oriented team the&lt;a href="https://www.thoughtworks.com/continuous-integration"&gt; continuous integration&lt;/a&gt; (CI) practise is.&lt;br&gt;
I’m pretty sure we can all agree on how CI tools support our day to day effectiveness. How they **might **save dozens of hours spent on non-essential tasks. Yet, it’s common to present CI tools as a hassle; slow, bulky, &lt;br&gt;
and unreliable pipelines bloated with chaotic events instead of fast, maintainable feedback loop configured to support both product quality &lt;br&gt;
and team flexibility.&lt;/p&gt;

&lt;p&gt;As the title implies, our CI process was far from optimal. We learned what “slow and chaotic” means the hard way. Below, you will find an overview &lt;br&gt;
of each issue that slowed us down, with full explanation of what the solution was (including code and external links), as well as honest &lt;strong&gt;results&lt;/strong&gt; measured by minutes.&lt;/p&gt;

&lt;p&gt;In this article, you will find discussion surrounding architecture, &lt;em&gt;flavour agnostic&lt;/em&gt; unit testing, Gradle usage as well as keeping your logs and artefacts deployment in order. Additionally, at the end of the article, several tips &lt;br&gt;
and tricks beyond optimisation will be included. &lt;br&gt;
It’s not a step-by-step tutorial. We gathered results that work for us, &lt;br&gt;
and &lt;strong&gt;you&lt;/strong&gt; have to **think them through. **If those solutions make sense to you then, and only then, apply them to your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The landscape
&lt;/h2&gt;

&lt;p&gt;In order to fully understand &lt;strong&gt;why&lt;/strong&gt; we provided a particular optimisation &lt;br&gt;
it is crucial to understand how our landscape looked at the time.&lt;/p&gt;

&lt;p&gt;There is&lt;a href="https://nvie.com/posts/a-successful-git-branching-model/"&gt; git flow approach&lt;/a&gt; in place, which usually means multiple &lt;em&gt;feature *branches exist at the same time in a remote repository. There is at least one *pull request&lt;/em&gt; per story. Each pull request needs to go through an integration* process* meaning the newest commit in a pull request triggers a fresh CI build. That’s being done in order to ensure the newest change won’t introduce any flaws. Yep, automation and unit test suites test each &lt;em&gt;software incrementation&lt;/em&gt;. Software Engineers in Test (SET) writes automation tests &lt;br&gt;
as “a part of“ the feature in some cases.&lt;/p&gt;

&lt;p&gt;We are supporting multiple modules as a part of our architecture. &lt;br&gt;
Let’s assume it is a&lt;a href="https://five.agency/android-architecture-part-2-clean-architecture/"&gt; clean-ish architecture&lt;/a&gt; with &lt;em&gt;domain&lt;/em&gt;, &lt;em&gt;data&lt;/em&gt; and &lt;em&gt;app&lt;/em&gt; layers packed into separate modules. Each of the modules has its own unit tests suite — between dozens to few hundreds of them per module. We have to support multiple &lt;em&gt;flavours&lt;/em&gt; and they differ greatly. Each flavour has &lt;br&gt;
a separate set of automation and unit test, although most of them are shared.&lt;/p&gt;

&lt;p&gt;When it comes to infrastructure, there is a separate Bitrise workflow for every build type. Also, a separate one for each of: &lt;em&gt;feature&lt;/em&gt; development, &lt;br&gt;
&lt;em&gt;automation&lt;/em&gt; efforts, &lt;em&gt;release&lt;/em&gt; (&lt;em&gt;tags&lt;/em&gt;) activities and after merging feature &lt;br&gt;
to the develop. Seeing how many distinct configs we have, there is a need &lt;br&gt;
to run multiple builds every day. We can’t and won’t have “infinite” amount &lt;br&gt;
of concurrent jobs, so time devoted to each build is very important to us. &lt;br&gt;
It’s also important because we &lt;strong&gt;value sh*t done the right way&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The basic measurement that &lt;strong&gt;will prove effectiveness here is build time&lt;/strong&gt; — both &lt;em&gt;entire&lt;/em&gt; build time or a particular step time (such as unit tests step or deploy step).&lt;/p&gt;

&lt;h2&gt;
  
  
  Improvements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Unit testing
&lt;/h3&gt;

&lt;p&gt;The most commonly used feedback loop is unit tests suite, in particular &lt;br&gt;
if you’re supporting multiple flavours for Android app and you want to be sure that none of the changes would break any of the flavours. &lt;br&gt;
Unit tests are supposed to be a fast and reliable feedback loop, which can be automated at the CI level. So, we used docs and tutorials to set them up for &lt;br&gt;
all of the flavours. After few changes to CI, we ended up with 30 minutes long unit test step for 3 flavours. &lt;br&gt;
Yes, you read it properly: 30 minutes for 3 flavours.&lt;/p&gt;

&lt;p&gt;Ok, let’s fix that.&lt;/p&gt;

&lt;p&gt;After a little bit of research it occurred to us that we used two separate steps for unit tests.&lt;a href="https://devcenter.bitrise.io/testing/android-run-a-unit-test/"&gt; Android unit test step for Bitrise&lt;/a&gt; was running &lt;em&gt;app&lt;/em&gt; module unit tests.&lt;a href="https://www.bitrise.io/integrations/steps/gradle-unit-test"&gt; Gradle Unit Test&lt;/a&gt; step was just running &lt;em&gt;.gradlew test&lt;/em&gt; task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--irHDeHES--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AZDxhzjlV0QFs2JDvktxfuA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--irHDeHES--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AZDxhzjlV0QFs2JDvktxfuA.png" alt="Total time for each step in minutes." width="681" height="87"&gt;&lt;/a&gt;&lt;em&gt;Total time for each step in minutes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What’s wrong with gradle unit test step in our case? According to the Gradle documentation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_TiFnbxR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2A6z7dHF2MnY1nAVjGlLGntQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_TiFnbxR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2A6z7dHF2MnY1nAVjGlLGntQ.png" alt="Source: [https://docs.gradle.org/current/userguide/command_line_interface.html](https://docs.gradle.org/current/userguide/command_line_interface.html)" width="880" height="136"&gt;&lt;/a&gt;&lt;em&gt;Source: &lt;a href="https://docs.gradle.org/current/userguide/command_line_interface.html"&gt;https://docs.gradle.org/current/userguide/command_line_interface.html&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In simple terms, &lt;em&gt;./gradlew test&lt;a href="https://medium.com/android-testing-daily/running-your-tests-on-the-command-line-with-gradle-bcba78244487"&gt;* triggers *&lt;em&gt;dozens **of different *test&lt;/em&gt; *tasks&lt;/a&gt;&lt;/em&gt; from every module. In our case, it triggered both &lt;em&gt;debug&lt;/em&gt; and &lt;em&gt;release&lt;/em&gt; related tests &lt;br&gt;
for every &lt;em&gt;subproject (*module&lt;/em&gt;)*. That’s too much redundancy; consider the final result of *./gradlew test *command:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(amount of flavours) x (amount of supported envs) x (amount of modules)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But the amount of tasks triggered is not all we can improve here. &lt;br&gt;
I already mentioned we have several modules. Since it’s &lt;em&gt;cleanish architecture, *it consists of *app&lt;/em&gt;, &lt;em&gt;domain&lt;/em&gt;, &lt;em&gt;data&lt;/em&gt; and &lt;em&gt;api&lt;/em&gt; modules. &lt;br&gt;
It’s easy to see that some of those modules are &lt;em&gt;flavour agnostic — *domain, data and api layers can and should be treated as libraries. Those are *external dependencies&lt;/em&gt; that &lt;em&gt;could&lt;/em&gt; be used via any JVM compatible code. &lt;br&gt;
Do we need to run those tests separately for each flavour? &lt;br&gt;
Of course we don’t! Where does it lead us?&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Flavour agnostic unit tests&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Split &lt;em&gt;flavour dependen*t and *flavour agnostic&lt;/em&gt; unit tests. Gain greater control over how your application is tested. Use&lt;a href="https://www.bitrise.io/integrations/steps/gradle-unit-test"&gt; Gradle Unit Test step&lt;/a&gt; in your&lt;a href="https://devcenter.bitrise.io/bitrise-cli/basics-of-bitrise-yml/"&gt; Bitrise.yml&lt;/a&gt; to run targeted &lt;em&gt;flavour agnostic&lt;/em&gt; unit tests, like this:&lt;/p&gt;

&lt;p&gt;Using &lt;em&gt;unit_test_task *attribute enables you to configure a particular task to be run. Basically, any gradle task. You can obviously chain gradle commands, but I want **granularity&lt;/em&gt;* here. Additionally, the usage of *title *attribute keeps build logs in order and enables you to track each step separately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---A12sm46--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ASQSkq3ZylChkaKHJxCGHTw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---A12sm46--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ASQSkq3ZylChkaKHJxCGHTw.png" alt="The result of applying the above mentioned recommendations. Cleaning up resources gave us unit tests result in seconds instead of minutes." width="678" height="123"&gt;&lt;/a&gt;&lt;em&gt;The result of applying the above mentioned recommendations. Cleaning up resources gave us unit tests result in seconds instead of minutes.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Flavour dependent unit tests
&lt;/h3&gt;

&lt;p&gt;The second recommendation relates to&lt;a href="https://devcenter.bitrise.io/testing/android-run-a-unit-test/"&gt; Android unit test step for Bitrise&lt;/a&gt; &lt;br&gt;
and how &lt;em&gt;flavour dependent&lt;/em&gt; unit tests are managed. In most cases, &lt;br&gt;
I would recommend you to run only what you need. But I came to conclusion that ‘run only what you need’ could be counterintuitive in our case.&lt;/p&gt;

&lt;p&gt;It’s &lt;strong&gt;really easy&lt;/strong&gt; to break one of the flavours by introducing changes to only one of them. That’s why we ended up with running unit tests for every flavour in every build. In addition, the above mentioned set of *flavour agnostic *tests is triggered. What does it mean when it comes to Bitrise CI setup?&lt;/p&gt;

&lt;p&gt;The above snippet runs unit tests for the &lt;em&gt;app *module for a particular flavour injected as an&lt;a href="https://devcenter.bitrise.io/builds/env-vars-secret-env-vars/"&gt; *environment variable&lt;/a&gt;&lt;/em&gt; and a particular build variant. &lt;br&gt;
So, if CI builds only one flavour at time, this snippet is supposed to be triggered three times, once for each flavour. If all of the flavours are built simultaneously, then each flavour should run its own unit tests in order to avoid redundancy and save a few minutes from build. Notice that, before &lt;em&gt;_UnitTestsPerFlavour step, UnitTests_Flavour_Agnostic_Modules *step is triggered. It runs flavour agnostic tests, so *domain&lt;/em&gt;, *data and feature *modules unit tests. Either way, all unit tests are always validated.&lt;/p&gt;

&lt;p&gt;Alternatively to the above setup, you can use the following setup to hardcode which flavour’s unit tests should be run:&lt;/p&gt;

&lt;p&gt;That way we’re all covered, no matter if we’re building all the flavours, or just one. Remember, the lesson here is &lt;em&gt;flavour dependent and agnostic&lt;/em&gt; unit tests should be triggered &lt;strong&gt;once.&lt;/strong&gt; There is no redundancy, but there is full coverage. Every software increment is &lt;strong&gt;safe&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;p&gt;We started with around &lt;strong&gt;30 minutes&lt;/strong&gt; per build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--irHDeHES--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AZDxhzjlV0QFs2JDvktxfuA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--irHDeHES--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AZDxhzjlV0QFs2JDvktxfuA.png" alt="Total time for unit testing then." width="681" height="87"&gt;&lt;/a&gt;&lt;em&gt;Total time for unit testing then.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And finished up with the below results when running one flavour.&lt;br&gt;
&lt;strong&gt;Down to ~5/6 minutes per build.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r9y3kQkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2A-ah-XIlWh18Ac0-6z7JEXA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r9y3kQkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2A-ah-XIlWh18Ac0-6z7JEXA.jpeg" alt="Total time for unit testing now." width="678" height="238"&gt;&lt;/a&gt;&lt;em&gt;Total time for unit testing now.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And also **down to ~3 minutes per build **when running all the flavours &lt;br&gt;
at once, which means each flavour is responsible for its own unit tests finally. Yes, that’s a separate config in order to optimise build time even further.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--niPGN7kL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AeFTwp0OYl-5lIMLpKgx3GA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--niPGN7kL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AeFTwp0OYl-5lIMLpKgx3GA.png" alt="Total time when running all of the flavours. Build time for those unit tests per one flavour." width="678" height="162"&gt;&lt;/a&gt;&lt;em&gt;Total time when running all of the flavours. Build time for those unit tests per one flavour.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Artefacts Deployment
&lt;/h2&gt;

&lt;p&gt;Review your D*eploy to Bitrise.io *step. According to the documentation&lt;a href="https://devcenter.bitrise.io/testing/test-reports/"&gt; [1]&lt;/a&gt; &lt;a href="https://www.bitrise.io/integrations/steps/deploy-to-bitrise-io"&gt;[2]&lt;/a&gt; &lt;a href="https://devcenter.bitrise.io/testing/device-testing-for-android/"&gt;[3]&lt;/a&gt; for the following steps test reports are deployed automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Xcode Test for iOS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Android Unit Test&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;iOS Device Testing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Virtual Device Testing for Android&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As noted in the documentation, by default Android unit and UI tests are deployed to Bitrise directory and are provided via the &lt;em&gt;Test reports&lt;/em&gt; tab. They are easily accessible — but the question is — are they really necessary?&lt;/p&gt;

&lt;p&gt;We have robust unit tests. They fail rarely in CI because the entire team writes and runs them frequently. On the other hand, it’s easy to check Bitrise for which logs failed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deciding what to deploy
&lt;/h3&gt;

&lt;p&gt;We already changed from&lt;a href="https://devcenter.bitrise.io/testing/android-run-a-unit-test/"&gt; Android unit test step for Bitrise&lt;/a&gt; to&lt;a href="https://www.bitrise.io/integrations/steps/gradle-unit-test"&gt; Gradle Unit Test&lt;/a&gt; step which does not deploy unit tests reports automatically. And we want it that way. What about the rest of the artefacts? For *automation *builds we’ve decided not to deploy any APKs. They are not needed.&lt;/p&gt;

&lt;p&gt;We also already know that&lt;a href="https://www.bitrise.io/integrations/steps/virtual-device-testing-for-android"&gt; Virtual Device Testing for Android&lt;/a&gt; step deploys UI tests results into the &lt;em&gt;Test Reports&lt;/em&gt; directory. We decided that for all of the builds we are going to move or remove D*eploy to Bitrise.io &lt;em&gt;step completely as an experiment. &lt;br&gt;
Also, D*eploy to Bitrise.io *step is always triggered before unit tests but after APK creation. That way, only application (*uatRelease&lt;/em&gt; APK for example) and the UI tests report are deployed.&lt;/p&gt;

&lt;p&gt;Initially &lt;em&gt;deploy to Bitrise.io *&lt;/em&gt;&lt;em&gt;step took from 2.1 to 3.2 minutes&lt;/em&gt;*.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IIQQuSzp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ArPm9k8yro36t8KsQr9f1oQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IIQQuSzp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ArPm9k8yro36t8KsQr9f1oQ.png" alt="Initially 3.2 min was total time per this step." width="679" height="51"&gt;&lt;/a&gt;&lt;em&gt;Initially 3.2 min was total time per this step.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After the changes it’s 0 minutes for some builds. It is ~8 seconds for most of them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cOZAitnF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AQkK3s1i0SLgx5Zo1NBNw0A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cOZAitnF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AQkK3s1i0SLgx5Zo1NBNw0A.png" alt="That’s how quick it could be!" width="679" height="48"&gt;&lt;/a&gt;&lt;em&gt;That’s how quick it could be!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7IqFQDy---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AkFe06gXR1oM1b5rbmSR9BQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7IqFQDy---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AkFe06gXR1oM1b5rbmSR9BQ.jpeg" alt="Oh yeah! Source: [https://knowyourmeme.com/photos/988454-we-did-it-reddit](https://knowyourmeme.com/photos/988454-we-did-it-reddit)" width="476" height="313"&gt;&lt;/a&gt;&lt;em&gt;Oh yeah! Source: &lt;a href="https://knowyourmeme.com/photos/988454-we-did-it-reddit"&gt;https://knowyourmeme.com/photos/988454-we-did-it-reddit&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation workflow
&lt;/h2&gt;

&lt;p&gt;One of the low hanging fruits was to change what is being done &lt;br&gt;
as a part of a particular workflow, since they all have different goals.&lt;br&gt;
As I mentioned, we have &lt;em&gt;feature&lt;/em&gt;, &lt;em&gt;automation&lt;/em&gt;, &lt;em&gt;develop&lt;/em&gt; and &lt;em&gt;release&lt;/em&gt; workflow.&lt;br&gt;
In our case, initially, all of the mentioned workflows had basically &lt;br&gt;
the same setup. Why is this wrong? Because, as we said, workflows simply have different &lt;strong&gt;responsibilities&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Understanding the differences in workflows&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I have already mentioned the &lt;em&gt;automation&lt;/em&gt; workflow. It’s because it is special compared to other workflows. The only responsibility automation workflow has is to support Software Engineers in Test in writing and securing automation test suite. That simple conclusion means we can trim &lt;br&gt;
several steps from it; in our case, APK and other artefacts creation &lt;br&gt;
and deployment. We were also able to get rid of custom scripts we had there for the release app or “runtime” resources optimisations steps and beyond.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;p&gt;By doing this, the *automation *build is a fast feedback loop for the SETs.&lt;br&gt;
**It takes around 10 minutes less than other builds.&lt;br&gt;
**I believe it’s a huge win for the SETs team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Investigating tools configuration
&lt;/h2&gt;

&lt;p&gt;Here is a quick and simple story as an example. Our builds produced &lt;em&gt;uatDebug&lt;/em&gt; and &lt;em&gt;uatRelease&lt;/em&gt; APKs. UAT stands for ‘&lt;a href="https://en.wikipedia.org/wiki/Acceptance_testing#User_acceptance_testing"&gt;user acceptance testing&lt;/a&gt;’ and it’s also a name of one of our environment s— environment with almost &lt;em&gt;production&lt;/em&gt; setup but more over &lt;em&gt;development&lt;/em&gt; data — and simply used for testing purposes. So, producing those two build sounds about right, doesn’t it? I started asking questions anyway. We were sure we need &lt;em&gt;uatRelease&lt;/em&gt; for testing purposes. It makes sense since testing production ready app (&lt;em&gt;release&lt;/em&gt;) using development data (&lt;em&gt;uat&lt;/em&gt;) is one of the best practices. But why do we need *uatDebug *then?&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Trimming unused resources&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The sole reason was a misconfiguration of the &lt;a href="https://www.charlesproxy.com/"&gt;Charles proxy&lt;/a&gt; tool, which led testers to not being able to use proxy tools while testing &lt;em&gt;uatRelease *build variant. Famous&lt;a href="https://developer.android.com/training/articles/security-config"&gt; *network_security_config&lt;/a&gt; *file had been added to the project but it wasn’t working, since the build variant has to be *debuggable. *The quick fix was to add *android:debuggable&lt;/em&gt; attribute to all &lt;em&gt;uat *builds. And since we’re not testing *uat&lt;/em&gt; builds using any public channels — it’s secure enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;p&gt;A simple configuration fix to the existing toolset &lt;strong&gt;brought an 8 minutes time reduction to each build and fixed SETs headache.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  All numbers together
&lt;/h2&gt;

&lt;p&gt;In summary&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Unit tests time **down from 30 minutes to 3~6 minutes. **Depends on build type.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automation build cut off by &lt;strong&gt;another 10 minutes&lt;/strong&gt; through removing a few unnecessary steps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Artefacts deployment reduced &lt;strong&gt;from 2.1~3.2 minutes into 8 seconds!&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix to Charles configs gave us another &lt;strong&gt;8 minutes&lt;/strong&gt; — due &lt;em&gt;uatDebug&lt;/em&gt; build removal.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We were able to shorten builds by between &lt;strong&gt;48 minutes and 34 minutes &lt;br&gt;
per each build&lt;/strong&gt;. That was a huge win and relief as you can imagine!&lt;/p&gt;

&lt;p&gt;We obviously made some rookie mistakes. But the most important part &lt;br&gt;
is to learn from them. We were able to adapt quickly and we’re providing other small improvements since then. It can’t happen on a daily basis because we also need to deliver &lt;em&gt;business value&lt;/em&gt; to our clients — but with an appropriate plan in place, I’m sure you can do even more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips and tricks beyond optimisations
&lt;/h2&gt;

&lt;p&gt;Bitrise and its plugins’ documentation is quite limited. You will need to deep dive into the plugins code if you want to understand the platform fully. Plugins code is mostly open source — you can find links inside plugin documentation. In particular, review the&lt;a href="https://github.com/bitrise-steplib/steps-gradle-unit-test/blob/master/main.go"&gt; *main.go&lt;/a&gt;* file if you’re looking for attributes and parameters which could customise the build.&lt;/p&gt;

&lt;p&gt;Use&lt;a href="https://app.bitrise.io/cli"&gt; Bitrise CLI&lt;/a&gt; in your terminal in order to test configuration locally. It will save you a lot of time.&lt;/p&gt;

&lt;p&gt;Have as granular CI steps as possible. Use *title *attribute extensively. Greater readability — greater control over time. Solid foundations are the first step for future optimisation.&lt;/p&gt;

&lt;p&gt;Do what we haven’t done yet — introduce tools to measure build metrics automatically.&lt;/p&gt;

&lt;p&gt;Leverage version control since Bitrise is &lt;em&gt;similar&lt;/em&gt; to&lt;a href="https://en.wikipedia.org/wiki/Infrastructure_as_code"&gt; infrastructure as a code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s a separate story but in Tigerspike, we &lt;strong&gt;optimised APK size by 13%&lt;/strong&gt; during our internal hackathon day. You should be aware of best practises for Android app configuration. Get rid or optimise resources, configuration and APK size. These kinds of things are also impacting your build time: git pull, compilation and build time, tests, deploy time — these are some of many examples.&lt;/p&gt;

&lt;p&gt;Listen. Observe. Experiment. Formulate a plan and adopt only what’s needed for your team. Good luck!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sOfBvx9o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ABZQiSFYjEQF1yKdcpJuWXA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sOfBvx9o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ABZQiSFYjEQF1yKdcpJuWXA.png" alt="Thanks! Source: [http://123emoji.com/donald-duck-stickers-2-9606/](http://123emoji.com/donald-duck-stickers-2-9606/)" width="225" height="225"&gt;&lt;/a&gt;&lt;em&gt;Thanks! Source: &lt;a href="http://123emoji.com/donald-duck-stickers-2-9606/"&gt;http://123emoji.com/donald-duck-stickers-2-9606/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I hope you like this piece. As you can see I love a fast feedback loop — &lt;br&gt;
if you have any objections, comments or questions — please drop a comment or DM message.&lt;/p&gt;

&lt;p&gt;The article showcases what we have done for one of our projects in&lt;a href="https://tigerspike.com/"&gt; Tigerspike&lt;/a&gt;. We’re&lt;a href="https://tigerspike.com/join-us/"&gt; hiring&lt;/a&gt; — please mention my name! ;)&lt;/p&gt;

&lt;p&gt;If you want to reach me out, I’m based in Wrocław, Poland.&lt;br&gt;
I’m also visiting London from time to time.&lt;br&gt;
Here is my&lt;a href="https://pl.linkedin.com/in/maciejmalak"&gt; LinkedIn&lt;/a&gt; and&lt;a href="https://twitter.com/monkeydevspl"&gt; Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>android</category>
      <category>mobile</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
