<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Posts on afanasev.net</title><link>https://afanasev.net/posts/</link><description>Recent content in Posts on afanasev.net</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Thu, 23 Apr 2020 23:18:12 +0700</lastBuildDate><atom:link href="https://afanasev.net/posts/feed.xml" rel="self" type="application/rss+xml"/><item><title>Implement your own in-house Crashlytics for Android</title><link>https://afanasev.net/android/crashlytics/2020/04/23/in-house-crashlytics.html</link><pubDate>Thu, 23 Apr 2020 23:18:12 +0700</pubDate><guid>https://afanasev.net/android/crashlytics/2020/04/23/in-house-crashlytics.html</guid><description>&lt;h2 id="why"&gt;Why&lt;/h2&gt;
&lt;p&gt;For years, Crashlytics by Fabric was the gold standard for mobile crash reporting. It was fast, reliable, deeply integrated with Android tooling, and free. Teams adopted it without hesitation, and it became load-bearing infrastructure for mobile development at companies of every size.&lt;/p&gt;
&lt;p&gt;Then Google acquired Fabric.&lt;/p&gt;
&lt;p&gt;The migration from Fabric Crashlytics to Firebase Crashlytics was manageable for many teams, but it immediately raised a set of questions we could not get clear answers to. Firebase tools, as a rule, depend on Google Play Services — a hard requirement that excludes Huawei devices running HMS (Huawei Mobile Services) and the Chinese Android ecosystem more broadly, where GMS is not available. China represented a significant portion of our user base, and the prospect of running two parallel crash reporting systems — one for GMS markets, one for HMS — was operationally unappealing.&lt;/p&gt;</description></item><item><title>Filter out classes from JaCoCo report using annotaitons</title><link>https://afanasev.net/kotlin/jacoco/2020/04/10/jacoco-ignore-annotation.html</link><pubDate>Fri, 10 Apr 2020 16:26:00 +0700</pubDate><guid>https://afanasev.net/kotlin/jacoco/2020/04/10/jacoco-ignore-annotation.html</guid><description>&lt;p&gt;&lt;img alt="JaCoCo code coverage report with Generated annotation" loading="lazy" src="https://afanasev.net/assets/img/jacoco-code-coverage.png"&gt;&lt;/p&gt;
&lt;h2 id="story"&gt;Story&lt;/h2&gt;
&lt;p&gt;Engineering culture at scale is difficult to build and even harder to maintain. In our company, we made a deliberate decision to enforce code coverage as a merge requirement — a hard gate on the CI pipeline that prevented branches from being merged unless they met a minimum threshold. We chose 80%: every new or modified class had to be covered by unit tests to at least that level.&lt;/p&gt;</description></item><item><title>Exclude properties from toString() of Kotlin data class (Sekret appx.)</title><link>https://afanasev.net/kotlin/data-class/2019/08/13/kotlin-data-class-tostirng-hide.html</link><pubDate>Tue, 13 Aug 2019 15:00:00 +0700</pubDate><guid>https://afanasev.net/kotlin/data-class/2019/08/13/kotlin-data-class-tostirng-hide.html</guid><description>&lt;p&gt;&lt;em&gt;This post describes how to remove class properties from the &lt;code&gt;toString()&lt;/code&gt; method manually if you prefer not to use the &lt;a href="https://afanasev.net/kotlin-library/2019/08/13/sekret.html"&gt;Sekret&lt;/a&gt; compiler plugin.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Kotlin&amp;rsquo;s data class &lt;code&gt;toString&lt;/code&gt; is generated automatically to include every property defined in the primary constructor. This is convenient for debugging but problematic when some of those properties contain sensitive data — passwords, tokens, PII — that must not appear in logs, crash reports, or analytics pipelines.&lt;/p&gt;
&lt;p&gt;There is no built-in Kotlin mechanism to exclude a specific field from the generated &lt;code&gt;toString&lt;/code&gt; while keeping it in &lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;hashCode&lt;/code&gt;, and &lt;code&gt;copy&lt;/code&gt;. The language does not (yet) offer an annotation equivalent to &lt;code&gt;@Transient&lt;/code&gt; for serialization. The approaches below represent the practical trade-offs available in pure Kotlin, each with distinct implications for class semantics, mutability, and maintainability.&lt;/p&gt;</description></item><item><title>Sekret — risk-free toString() of Kotlin data class?</title><link>https://afanasev.net/kotlin-library/2019/08/13/sekret.html</link><pubDate>Tue, 13 Aug 2019 14:18:12 +0700</pubDate><guid>https://afanasev.net/kotlin-library/2019/08/13/sekret.html</guid><description>&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;
&lt;p&gt;One of Kotlin&amp;rsquo;s most appreciated features is the data class. With a single keyword, the compiler automatically generates &lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;hashCode&lt;/code&gt;, &lt;code&gt;copy&lt;/code&gt;, &lt;code&gt;componentN&lt;/code&gt;, and &lt;code&gt;toString&lt;/code&gt; — eliminating an entire category of boilerplate that Java developers had to write and maintain by hand. For domain objects, value types, and state containers, data classes are the obvious choice.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;toString&lt;/code&gt; function, in particular, is invaluable for debugging. When you log a data class instance, you get a complete, readable snapshot of its state: every field name and value, formatted cleanly. This is exactly what you want during development.&lt;/p&gt;</description></item><item><title>KSON — auto-generate GSON adapters for Kotlin data classes</title><link>https://afanasev.net/kotlin-library/2019/08/12/kson.html</link><pubDate>Mon, 12 Aug 2019 14:18:12 +0700</pubDate><guid>https://afanasev.net/kotlin-library/2019/08/12/kson.html</guid><description>&lt;p&gt;Kotlin data classes are an ideal fit for API response models. They are concise, immutable by default, and come with auto-generated &lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;hashCode&lt;/code&gt;, &lt;code&gt;copy&lt;/code&gt;, and &lt;code&gt;toString&lt;/code&gt; — everything you need from a value object representing a network payload. Pairing them with GSON is natural and widely practiced across the Android ecosystem.&lt;/p&gt;
&lt;p&gt;However, there is a subtle but serious trap hiding in that combination — one that silently violates Kotlin&amp;rsquo;s type safety guarantees and can cause crashes that are difficult to reproduce and frustrating to diagnose.&lt;/p&gt;</description></item></channel></rss>