# Blogs on Ryan Harter

Recent content in Blogs on Ryan Harter

This is one page of public article previews, not the complete archive. Follow Next page to continue. Summaries are not the original full articles.

## A Gradle Module Structure for Clear Boundaries, Faster Builds, and Test Coverage

DevFeed: [A Gradle Module Structure for Clear Boundaries, Faster Builds, and Test Coverage](<https://devfeed.tech/articles/a-great-gradle-module-structure-30613.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2026/07/a-great-gradle-module-structure/>)

Author: Jesse Wilson

Published: 2026-07-15T13:38:11Z

Content type: article

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Gradle](<https://devfeed.tech/topics/gradle.md>), [build times](<https://devfeed.tech/topics/build-times.md>), [modules](<https://devfeed.tech/topics/modules.md>), [test-coverage](<https://devfeed.tech/topics/test-coverage.md>), [Android Gradle Plugin](<https://devfeed.tech/topics/android-gradle-plugin.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>)

Tags: [android-gradle-plugin](<https://devfeed.tech/tags/android-gradle-plugin.md>), [build](<https://devfeed.tech/tags/build.md>), [build-times](<https://devfeed.tech/tags/build-times.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [modules](<https://devfeed.tech/tags/modules.md>), [test-coverage](<https://devfeed.tech/tags/test-coverage.md>)

### AI overview

The article describes a Gradle module convention that separates each feature's public API from its implementation. The author says this structure reduced build times and coupling between features while helping enforce test coverage, with Android Gradle Plugin support making a separate test-fixtures module unnecessary.

### Source excerpt

Flattening my Dependency Graph Jesse Wilson Jesse explores a Gradle module structure that he likes. Read now ➡ I liked this not-so-recent post from Jesse Wilson, which outlines a Gradle module convention that prioritizes clear public/private boundaries to support encapsulation and build speed. The approach, adapted from one presented by Ralf Wondratschek, also happens to closely resemble one that I helped institute at Dropbox shortly after I joined four and a half years ago.

## Open Source Maintainers Can Step Back Without Owing Ongoing Support

DevFeed: [Open Source Maintainers Can Step Back Without Owing Ongoing Support](<https://devfeed.tech/articles/graceful-exit-30612.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2026/06/graceful-exit/>)

Author: Mark

Published: 2026-06-19T01:44:39Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Open Source](<https://devfeed.tech/topics/open-source.md>)

Tags: [open-source](<https://devfeed.tech/tags/open-source.md>), [open-source-maintainer](<https://devfeed.tech/tags/open-source-maintainer.md>), [project](<https://devfeed.tech/tags/project.md>), [support](<https://devfeed.tech/tags/support.md>)

### AI overview

This opinion article reflects on open source maintainer burnout and argues that maintainers may step back when their lives or interests change. It emphasizes that open source projects are invitations to modify or continue, not promises of free ongoing support.

### Source excerpt

Public Mirror Mark (from unsigned.io) Mark explains why he is stepping back from public-facing interaction with his Reticulum projects, and invites anyone who wants to use or continue the project. Read now ➡ It's always sad, but not uncommon, to see some open source maintainer burn out. We build these projects for any number of personal reasons, we share them with the world. If we're lucky someone will find it interesting or useful. If we're really lucky someone will share our passion and we'll find or build a community.

## Incremental Maintenance

DevFeed: [Incremental Maintenance](<https://devfeed.tech/articles/incremental-maintenance-30611.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2026/05/incremental-maintenance/>)

Published: 2026-05-14T01:53:09Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [maintenance](<https://devfeed.tech/topics/maintenance.md>), [Dependency injection](<https://devfeed.tech/topics/dependency-injection.md>), [implementation](<https://devfeed.tech/topics/implementation.md>), [screen](<https://devfeed.tech/topics/screen.md>), [App](<https://devfeed.tech/topics/app.md>)

Tags: [core](<https://devfeed.tech/tags/core.md>), [dependencies](<https://devfeed.tech/tags/dependencies.md>), [dependency-injection](<https://devfeed.tech/tags/dependency-injection.md>), [incremental](<https://devfeed.tech/tags/incremental.md>), [maintenance](<https://devfeed.tech/tags/maintenance.md>), [patterns](<https://devfeed.tech/tags/patterns.md>), [product](<https://devfeed.tech/tags/product.md>), [screen](<https://devfeed.tech/tags/screen.md>)

### AI overview

An opinion article about incremental maintenance in a large, aging application. It argues that small refactoring choices can reduce entrenched patterns, using dependency injection to keep feature-gating details out of a monolithic tab-host screen.

### Source excerpt

I recently reviewed some code at work that made me pause. The change affected a part of the codebase that we affectionately call "The Monolith", which is some of the oldest code in our 16 year old app. While there are reasonable arguments that it's hard to tease apart this older hairball of code, there are often small choices that can either move you closer to the decoupled promised land, or further entrench you in bad patterns.

## Jake Wharton's Resume Communicates His Values and Work Style

DevFeed: [Jake Wharton's Resume Communicates His Values and Work Style](<https://devfeed.tech/articles/values-30610.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2026/01/values/>)

Published: 2026-01-31T04:05:39Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Job](<https://devfeed.tech/topics/job.md>), [RSS](<https://devfeed.tech/topics/rss.md>)

Tags: [job](<https://devfeed.tech/tags/job.md>), [resume](<https://devfeed.tech/tags/resume.md>), [rss](<https://devfeed.tech/tags/rss.md>), [work](<https://devfeed.tech/tags/work.md>)

### AI overview

The author praises Jake Wharton's resume for listing his values and clearly communicating what matters to him and how he prefers to work.

### Source excerpt

I was recently pruning and organizing my RSS subscriptions and indirectly came across Jake Wharton's hiring page. I knew he was on the job market, but I love the way he approaches this resume, by listing some of his values, clearly communicating what's important to him and how he likes to work. This kind of straight-forward honesty goes a long way.

## Simplifying a Proposed File-List Composable Through Architecture Review

DevFeed: [Simplifying a Proposed File-List Composable Through Architecture Review](<https://devfeed.tech/articles/celebrate-the-code-you-didn-t-write-30609.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2025/06/celebrate-the-code-you-didnt-write/>)

Published: 2025-06-06T03:47:36Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Code](<https://devfeed.tech/topics/code.md>), [Dependency injection](<https://devfeed.tech/topics/dependency-injection.md>), [interfaces](<https://devfeed.tech/topics/interfaces.md>), [App](<https://devfeed.tech/topics/app.md>)

Tags: [architecture](<https://devfeed.tech/tags/architecture.md>), [code](<https://devfeed.tech/tags/code.md>), [dependency-injection](<https://devfeed.tech/tags/dependency-injection.md>), [interfaces](<https://devfeed.tech/tags/interfaces.md>), [simplify](<https://devfeed.tech/tags/simplify.md>)

### AI overview

An architecture and tech-spec review of a proposed file-list Composable revealed that its dependency-injection machinery and added interfaces created unnecessary complexity. By examining use cases, letting callers construct data sources, and removing interfaces that added no value, the team simplified the design until the proposed code was eliminated.

### Source excerpt

Over the past few years I've been driving an architecture and tech spec review process at work. This has been helpful in breaking down certain silos, distributing knowledge across the organization, and building skills and experience in more junior engineers. A recent tech spec caught my eye, and resulted in a really valuable learning opportunity. The specifics of the spec aren't terribly important, but at a high level the proposal was to introduce a single Composable to represent lists of files anywhere in the app. Due to the multitude of sources that can provide lists of files this was accomplished with a relatively complex set of generic data source factories that were registered via our dependency injection system to later be looked up and constructed.

## The Value of Starting Unfinished Software Projects

DevFeed: [The Value of Starting Unfinished Software Projects](<https://devfeed.tech/articles/start-more-projects-30608.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2024/12/start-more-projects/>)

Published: 2024-12-07T14:42:34Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Programming](<https://devfeed.tech/topics/programming.md>), [Code](<https://devfeed.tech/topics/code.md>), [Git](<https://devfeed.tech/topics/git.md>), [API](<https://devfeed.tech/topics/api.md>), [multiplatform](<https://devfeed.tech/topics/multiplatform.md>), [XMPP](<https://devfeed.tech/topics/xmpp.md>), [Streaming](<https://devfeed.tech/topics/streaming.md>), [browser](<https://devfeed.tech/topics/browser.md>), [React](<https://devfeed.tech/topics/react.md>), [Web](<https://devfeed.tech/topics/web.md>), [Web app](<https://devfeed.tech/topics/webapp.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>)

Tags: [api](<https://devfeed.tech/tags/api.md>), [browser](<https://devfeed.tech/tags/browser.md>), [code](<https://devfeed.tech/tags/code.md>), [git](<https://devfeed.tech/tags/git.md>), [intellij-idea](<https://devfeed.tech/tags/intellij-idea.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [kotlin-multiplatform](<https://devfeed.tech/tags/kotlin-multiplatform.md>), [multiplatform](<https://devfeed.tech/tags/multiplatform.md>), [programming](<https://devfeed.tech/tags/programming.md>), [projects](<https://devfeed.tech/tags/projects.md>), [protocol](<https://devfeed.tech/tags/protocol.md>), [react](<https://devfeed.tech/tags/react.md>), [streaming](<https://devfeed.tech/tags/streaming.md>)

### AI overview

The author argues that unfinished software projects and experimental branches can be valuable even when they never become published products. Small coding experiments help answer API and implementation questions, explore framework and library designs, and build practical experience.

### Source excerpt

A couple of months ago I was pair programming with a colleague, sharing my screen, and he was amazed to see the number of branches I had in my git repo. This wasn't simply a case of poor git hygene, these were branches that I had started but never pushed, branches that were never merged. As I explained this I realized that my attitude towards starting and finishing projects has changed dramatically over the years.

## Making Feature-Gated Code Easier to Remove

DevFeed: [Making Feature-Gated Code Easier to Remove](<https://devfeed.tech/articles/make-things-easy-30607.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2024/07/make-things-easy/>)

Published: 2024-07-25T13:07:35Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Code](<https://devfeed.tech/topics/code.md>), [Development](<https://devfeed.tech/topics/development.md>), [legacy](<https://devfeed.tech/topics/legacy.md>), [implementation](<https://devfeed.tech/topics/implementation.md>), [App](<https://devfeed.tech/topics/app.md>), [A/B Testing](<https://devfeed.tech/topics/a-b-testing.md>)

Tags: [a-b-testing](<https://devfeed.tech/tags/a-b-testing.md>), [app](<https://devfeed.tech/tags/app.md>), [code](<https://devfeed.tech/tags/code.md>), [development](<https://devfeed.tech/tags/development.md>), [implementation](<https://devfeed.tech/tags/implementation.md>), [legacy](<https://devfeed.tech/tags/legacy.md>)

### AI overview

This commentary explains the benefits and cleanup costs of feature gates. It argues that planning cleanup and minimizing branching can make legacy gated code easier and safer to remove after a rollout.

### Source excerpt

I've been using feature gates for a long time. Having the ability to conditionally alter the behavior of an app unlocks so many benefits that I wouldn't go back. I love being able to have a consistent release train by keeping in-progress features locked behind a gate, being able to validate the utility and impact of the features I work on using A/B testing, and being able to mitigate the risk of rolling out new features. But feature gates also come with downsides, especially as a codebase and organization ages.

## Extending Coil

DevFeed: [Extending Coil](<https://devfeed.tech/articles/extending-coil-30606.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2024/04/extending-coil/>)

Published: 2024-04-12T01:53:35Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Library](<https://devfeed.tech/topics/library.md>), [SDKs](<https://devfeed.tech/topics/sdks.md>), [Back end](<https://devfeed.tech/topics/backend.md>), [Code](<https://devfeed.tech/topics/code.md>), [Open Source](<https://devfeed.tech/topics/open-source.md>)

Tags: [backend](<https://devfeed.tech/tags/backend.md>), [image](<https://devfeed.tech/tags/image.md>), [implement](<https://devfeed.tech/tags/implement.md>), [library](<https://devfeed.tech/tags/library.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [sdk](<https://devfeed.tech/tags/sdk.md>)

### AI overview

This tutorial explains how to extend the Coil image-loading library to load file thumbnails from a custom backend SDK. It covers custom data sources, data types, fetchers, and mappers so applications can pass lightweight file identifiers directly to Coil and reduce view-model overhead.

### Source excerpt

I was recently working on showing thumbnails for files, and found that the image loading code was surprisingly complex. I load thumbnails from my backend service via an SDK, which means I don't have a simple URI that I can provide to an image loading library to load images. Added to that is the fact that I usually pass around a lightweight file id to represent files within my app, but my backend also requires a revision to generate a thumbnail, even though I always simply want the latest revision.

## Screenshot Tests Are Manual Tests

DevFeed: [Screenshot Tests Are Manual Tests](<https://devfeed.tech/articles/screenshot-tests-are-manual-tests-30605.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2023/10/screenshot-tests-are-manual-tests/>)

Published: 2023-10-17T16:00:00Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [screenshot-testing](<https://devfeed.tech/topics/screenshot-testing.md>), [Testing](<https://devfeed.tech/topics/testing.md>), [ui-testing](<https://devfeed.tech/topics/ui-testing.md>), [test-coverage](<https://devfeed.tech/topics/test-coverage.md>)

Tags: [code-coverage](<https://devfeed.tech/tags/code-coverage.md>), [screenshot-testing](<https://devfeed.tech/tags/screenshot-testing.md>), [testing](<https://devfeed.tech/tags/testing.md>), [ui-testing](<https://devfeed.tech/tags/ui-testing.md>)

### AI overview

The article explains that screenshot tests validate UI appearance and can reveal visual changes, but they do not validate application behavior. It argues that screenshot-based coverage can increase coverage numbers while still requiring manual review and complementary UI testing.

### Source excerpt

When I joined Dropbox almost 2 years ago, I was asked to help improve the screenshot testing experience, resulting in Dropshots. While I'd played around with early versions of Paparazzi by that point, many of our tests were too heavily reliant on full Activities and Fragments to make use of host side JUnit tests. The more I've worked with screenshot tests, the more I've come to understand their promises and limitations. What screenshot tests offer Screenshot testing can be really helpful to validate that elements on the screen are rendered as you expect. They can help you detect visual changes that might have otherwise gone unnoticed.

## Why Dagger's Provider Interface Is Simple and Flexible

DevFeed: [Why Dagger's Provider Interface Is Simple and Flexible](<https://devfeed.tech/articles/simple-apis-are-elegant-apis-30604.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2023/08/simple-apis-are-elegant-apis/>)

Published: 2023-08-10T02:55:50Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Dagger](<https://devfeed.tech/topics/dagger.md>), [Dependency injection](<https://devfeed.tech/topics/dependency-injection.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [apis](<https://devfeed.tech/tags/apis.md>), [dagger](<https://devfeed.tech/tags/dagger.md>), [dependency-injection](<https://devfeed.tech/tags/dependency-injection.md>), [interface](<https://devfeed.tech/tags/interface.md>), [jvm](<https://devfeed.tech/tags/jvm.md>)

### AI overview

The article examines how Dagger's javax.inject.Provider interface supports generated dependency providers and flexible object-creation strategies. It discusses shared instances, lazy and eager instantiation, and lifecycle scoping.

### Source excerpt

I recently gave a presentation about how Dagger works under the hood, and I was once again struck by the elegance of the javax.inject.Provider interface. The interface is so simple it almost seems useless, but it's also incredibly flexible, and forms the basis of much of the code generated by Dagger. Like many dependency injection frameworks for JVM languages, Dagger uses and builds on the standard set of annotations for injectable classes defined in JSR-330 and provided in the javax.inject package.

## How Android and iOS Handle Rounded Rectangle Corners

DevFeed: [How Android and iOS Handle Rounded Rectangle Corners](<https://devfeed.tech/articles/rounded-corners-30603.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2022/09/rounded-corners/>)

Published: 2022-09-20T03:37:48Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Graphics](<https://devfeed.tech/topics/graphics.md>), [Android](<https://devfeed.tech/topics/android.md>), [iOS](<https://devfeed.tech/topics/ios.md>), [GUI](<https://devfeed.tech/topics/gui.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [graphics](<https://devfeed.tech/tags/graphics.md>), [ios](<https://devfeed.tech/tags/ios.md>), [ui](<https://devfeed.tech/tags/ui.md>)

### AI overview

The article examines how Android and iOS render rectangles with corner radii larger than half the shortest edge. It explains that Android restricts corner radii to half the shortest edge, while Apple's approach applies each corner radius individually and uses the resulting circle intersections.

### Source excerpt

The other day I happened to notice an interesting question that Michał Zieliński shared in an Android Graphics channel that I frequent. He had observed that if he has a 40dp square and sets a 30dp corner radius, on Android the resulting shape looks the same as a 20dp corner radius. He then concluded that, unlike iOS, Android appears to clamp the corner radii of a rectangle to half of the shortest edge length.

## Building Plugable Features on Android

DevFeed: [Building Plugable Features on Android](<https://devfeed.tech/articles/building-plugable-features-on-android-30602.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2022/08/building-plugable-features-on-android/>)

Published: 2022-08-07T15:05:54Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Gradle](<https://devfeed.tech/topics/gradle.md>), [App](<https://devfeed.tech/topics/app.md>), [configuration](<https://devfeed.tech/topics/configuration.md>), [screen](<https://devfeed.tech/topics/screen.md>), [feature flags](<https://devfeed.tech/topics/feature-flags.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [building](<https://devfeed.tech/tags/building.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [dependencies](<https://devfeed.tech/tags/dependencies.md>), [developer](<https://devfeed.tech/tags/developer.md>), [feature-flags](<https://devfeed.tech/tags/feature-flags.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [modularization](<https://devfeed.tech/tags/modularization.md>)

### AI overview

This article discusses modularizing Android projects with Gradle and designing reusable developer settings features. It describes aggregating feature dependencies and moving each feature's debug configuration alongside the code it configures.

### Source excerpt

As Gradle projects and their teams grow, modularization becomes an important tool to help ensure developers can continue to be productive without stepping on each other's toes. This can lead to some interesting architectural challenges, however, when creating features that require aggregating dependencies. One example of a feature like this could be a developer settings screen, which allows configuration of different features within an app, and whose features might change between apps.

## Using ShaderToy to Prototype a Graphics Idea Before Integrating It into an App

DevFeed: [Using ShaderToy to Prototype a Graphics Idea Before Integrating It into an App](<https://devfeed.tech/articles/make-a-prototype-30601.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2020/12/make-a-prototype/>)

Published: 2020-12-16T16:08:53Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Code](<https://devfeed.tech/topics/code.md>), [shaders](<https://devfeed.tech/topics/shaders.md>), [Graphics](<https://devfeed.tech/topics/graphics.md>), [Web](<https://devfeed.tech/topics/web.md>), [App](<https://devfeed.tech/topics/app.md>), [Mobile](<https://devfeed.tech/topics/mobile.md>)

Tags: [audio](<https://devfeed.tech/tags/audio.md>), [experimental](<https://devfeed.tech/tags/experimental.md>), [graphics](<https://devfeed.tech/tags/graphics.md>), [mobile](<https://devfeed.tech/tags/mobile.md>), [performance](<https://devfeed.tech/tags/performance.md>), [prototype](<https://devfeed.tech/tags/prototype.md>), [shaders](<https://devfeed.tech/tags/shaders.md>), [web](<https://devfeed.tech/tags/web.md>)

### AI overview

The article explains how to use a lightweight ShaderToy prototype to investigate a graphics idea before investing time in a larger native app. The author used it to test a brush-stroke concept for Pigment, quickly confirm that the approach could work, and assess whether its performance might be feasible on high-resolution mobile hardware.

### Source excerpt

When coming up with ideas for new ways to solve problems in my code, there are usually a number of unknowns. As I brainstorm solutions, I don't mind a bit of hand-wavy magic over certain portions of the solution, but I find it valuable to be honest about which parts I'm sure of, and which parts will need some more investigation. I was recently thinking about an experimental brushing engine that I'm working on for Pigment, the coloring book app that I work on, and had an idea for a new way to draw brush strokes that had the potential to use far less memory and be able to draw faster. While I had the rough idea for how this drawing would work, I wasn't exactly sure if the code could be structure in a way that would give me the performance gains I had hoped for, so it was time for a prototype.

## Handling Dates & Times in SQLite

DevFeed: [Handling Dates & Times in SQLite](<https://devfeed.tech/articles/handling-dates-times-in-sqlite-30600.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2020/09/handling-dates-times-in-sqlite/>)

Published: 2020-09-04T16:35:01Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [SQLite](<https://devfeed.tech/topics/sqlite.md>), [Databases](<https://devfeed.tech/topics/databases.md>), [DateTime](<https://devfeed.tech/topics/datetime.md>), [SQL](<https://devfeed.tech/topics/sql.md>), [timezone](<https://devfeed.tech/topics/timezone.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>)

Tags: [database](<https://devfeed.tech/tags/database.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [sql](<https://devfeed.tech/tags/sql.md>), [sqlite](<https://devfeed.tech/tags/sqlite.md>), [time](<https://devfeed.tech/tags/time.md>), [timezone](<https://devfeed.tech/tags/timezone.md>)

### AI overview

The article examines how to store dates and times in SQLite for a Kotlin app using Room. It explains that SQLite has no dedicated DateTime type and discusses problems with storing timestamps as Unix-epoch milliseconds, including timezone handling and query support.

### Source excerpt

As I've been refactoring a fairly large section of Pigment, my coloring book app for adults, I came across some SQL code which I haven't touched in a very long time and didn't seem quite right. Part of Pigment's large library of coloring books and pages are the Daily books, which are a different book each month in which a new page is released each day. The day's page is free only for the day (for non-subscribers), and then reverts to being locked once the next page is released. Users can unlock a page forever by simply starting a coloring project with it while it's free, but there is a limited window in which it's available.

## SQLDelight on the Server

DevFeed: [SQLDelight on the Server](<https://devfeed.tech/articles/sqldelight-on-the-server-30599.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2020/08/sqldelight-on-the-server/>)

Published: 2020-08-27T16:05:00Z

Content type: article

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Object-relational mapping](<https://devfeed.tech/topics/orm.md>), [SQL](<https://devfeed.tech/topics/sql.md>), [MySQL](<https://devfeed.tech/topics/mysql.md>), [Databases](<https://devfeed.tech/topics/databases.md>), [API](<https://devfeed.tech/topics/api.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [api](<https://devfeed.tech/tags/api.md>), [mariadb](<https://devfeed.tech/tags/mariadb.md>), [mysql](<https://devfeed.tech/tags/mysql.md>), [orm](<https://devfeed.tech/tags/orm.md>), [postgresql](<https://devfeed.tech/tags/postgresql.md>), [sql](<https://devfeed.tech/tags/sql.md>), [sqldelight](<https://devfeed.tech/tags/sqldelight.md>)

### AI overview

The article explains how SQLDelight evolved from an Android ORM into a multiplatform database access tool that supports server-grade dialects such as MySQL, MariaDB, PostgreSQL, and H2. It also describes using SQLDelight with MySQL in a currency conversion API called Gringotts.

### Source excerpt

I've used Cashapp's SQLDelight for years in my Android apps. Unlike other ORM libraries, which create an abstraction that allows you to access databases in native code and generates the SQL to access the data for you, SQLDelight embraces SQL as a first class language, recognizing it's optimizations for defining and querying data, and allows you to write queries and data definitions and generates consuming code based on those. Over the last few years SQLDelight has been transformed from a nice Android ORM that integrates nicely with AutoValue into a mutliplatform option to access and manage your data without having to learn another query abstraction.

## Server Side Analytics with GoAccess

DevFeed: [Server Side Analytics with GoAccess](<https://devfeed.tech/articles/server-side-analytics-with-goaccess-30598.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2020/06/server-side-analytics-with-goaccess/>)

Published: 2020-06-21T09:01:33Z

Content type: article

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Website](<https://devfeed.tech/topics/website.md>), [Server](<https://devfeed.tech/topics/server.md>), [Web Development](<https://devfeed.tech/topics/web-development.md>)

Tags: [analytics](<https://devfeed.tech/tags/analytics.md>), [cloudflare](<https://devfeed.tech/tags/cloudflare.md>), [firebase](<https://devfeed.tech/tags/firebase.md>), [github-actions](<https://devfeed.tech/tags/github-actions.md>), [google-analytics](<https://devfeed.tech/tags/google-analytics.md>), [hugo](<https://devfeed.tech/tags/hugo.md>), [privacy](<https://devfeed.tech/tags/privacy.md>), [server](<https://devfeed.tech/tags/server.md>)

### AI overview

The author explains replacing Google Analytics on a hobby Hugo site with server-side analytics based on access logs. The approach can show page requests and limited aggregate information such as readers' operating systems without using browser-based JavaScript to track individuals across the internet. Cloudflare's analytics were considered but did not provide page-level access numbers, leading the author to GoAccess.

### Source excerpt

When I updated my blog about a month ago, my primary goals were to simplify and modernize things. I updated to the latest version of Hugo, which powers by blog, added automatic deployment of new posts using GitHub Actions, and moving away from my previous hosting provider, Firebase, which was far more than was needed to host my simple, static site. One of those simplifications was removing Google Analytics from my site. I, like many others, have become more and more aware of the privacy trade offs that I've often overlooked in technology. Once I examined my need for Google Analytics in my site it became clear that I hardly even use it, and decided that it isn't worth pumping information about the behavioral patterns of my readers into Google's big data machine.

## Replacing Mocks

DevFeed: [Replacing Mocks](<https://devfeed.tech/articles/replacing-mocks-30597.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2020/06/replacing-mocks/>)

Published: 2020-06-09T07:23:09Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Testing](<https://devfeed.tech/topics/testing.md>), [maintenance](<https://devfeed.tech/topics/maintenance.md>), [Programming](<https://devfeed.tech/topics/programming.md>)

Tags: [implementation](<https://devfeed.tech/tags/implementation.md>), [maintenance](<https://devfeed.tech/tags/maintenance.md>), [testing](<https://devfeed.tech/tags/testing.md>), [tests](<https://devfeed.tech/tags/tests.md>)

### AI overview

The article explains why the author increasingly avoids mock objects in tests. It argues that mocks can encourage testing implementation details, producing brittle tests that require ongoing maintenance, and discusses replacing mocks with fakes while noting that wrapping a mock may not solve the underlying problem.

### Source excerpt

Sam Edwards recently posted his strategy for wrapping mock objects to avoid using verbose syntax and facilitate reuse. After reading the post, my first thought was that this example comes really close to showing my strategy for removing mock objects from tests. Why Remove the Mocks There has been endless discussion about the benefits and drawbacks of Mock objects in testing. As I've become better at testing, and programming in general, I've come to use mock objects less and less, even removing them from my existing projects as I come across them.

## Your Labor is a Product

DevFeed: [Your Labor is a Product](<https://devfeed.tech/articles/your-labor-is-a-product-30596.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2020/05/your-labor-is-a-product/>)

Published: 2020-05-26T14:21:05Z

Content type: opinion

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Job](<https://devfeed.tech/topics/job.md>), [Software](<https://devfeed.tech/topics/software.md>), [Android](<https://devfeed.tech/topics/android.md>), [cloud-infrastructure](<https://devfeed.tech/topics/cloud-infrastructure.md>)

Tags: [employment](<https://devfeed.tech/tags/employment.md>), [freelance](<https://devfeed.tech/tags/freelance.md>), [job](<https://devfeed.tech/tags/job.md>), [margin](<https://devfeed.tech/tags/margin.md>), [money](<https://devfeed.tech/tags/money.md>), [remote](<https://devfeed.tech/tags/remote.md>), [software-developer](<https://devfeed.tech/tags/software-developer.md>), [tech-industry](<https://devfeed.tech/tags/tech-industry.md>)

### AI overview

The author argues that employment can be understood as a market transaction in which workers sell their labor for a salary. Drawing on freelance Android development and remote work, the article examines how internet-enabled delivery and location-related costs affect labor pricing.

### Source excerpt

Before joining Pixite I spent several years as a freelance Android developer. Being 100% remote for all of those years, I learned many things about the process of hiring and employment. The biggest eye-opener for me was the realization that when you apply and interview for a job, you are selling a product. Though the language is often about employers providing perks to "sell you" on the idea of working there, the actual relationship is the opposite.

## Easy Android Scopes

DevFeed: [Easy Android Scopes](<https://devfeed.tech/articles/easy-android-scopes-30595.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2020/03/easy-android-scopes/>)

Published: 2020-03-13T16:09:47Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [configuration](<https://devfeed.tech/topics/configuration.md>), [Dagger](<https://devfeed.tech/topics/dagger.md>), [multiplatform](<https://devfeed.tech/topics/multiplatform.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-framework](<https://devfeed.tech/tags/android-framework.md>), [cache](<https://devfeed.tech/tags/cache.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [dagger](<https://devfeed.tech/tags/dagger.md>), [deprecated](<https://devfeed.tech/tags/deprecated.md>), [fragments](<https://devfeed.tech/tags/fragments.md>), [lifecycle](<https://devfeed.tech/tags/lifecycle.md>), [multiplatform](<https://devfeed.tech/tags/multiplatform.md>)

### AI overview

This tutorial examines how to scope Android objects across lifecycle changes, especially when Activities and Fragments are recreated during configuration changes. It discusses retaining presenters and extending lifecycle-bound retention to objects such as Dagger Components, while noting the limitations of the deprecated onRetainNonConfigurationInstance approach.

### Source excerpt

A common challenge when writing Android apps is properly scoping objects: if they outlive the lifecycle in which they're needed then we are wasting memory and they can be leaked, but if we recreate objects too soon then we could be duplicating work when we don't need to. The most common place that this manifests in is Activities and Fragments, since they're destroyed and replaced by a new instance during events like configuration changes, which can be unintuitive, at first.

## Handling Transient Events

DevFeed: [Handling Transient Events](<https://devfeed.tech/articles/handling-transient-events-30594.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2019/07/handling-transient-events/>)

Published: 2019-07-26T15:00:00Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [App](<https://devfeed.tech/topics/app.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [events](<https://devfeed.tech/tags/events.md>), [lifecycle](<https://devfeed.tech/tags/lifecycle.md>), [livedata](<https://devfeed.tech/tags/livedata.md>), [navigation](<https://devfeed.tech/tags/navigation.md>), [notifications](<https://devfeed.tech/tags/notifications.md>), [view](<https://devfeed.tech/tags/view.md>)

### AI overview

This article explains why LiveData is well suited to persistent view state but less suited to transient view events. It distinguishes state that should be saved and restored from one-time events such as notifications, navigation, dialogs, and errors, using an Android app example.

### Source excerpt

LiveData has become an important part of many Android apps. With it's elegant handling of the complex lifecycle inherent in many Android components, its understandable that we would try to use them everywhere we can. LiveData, however, was designed with a very specific use case in mind. Its intended to hold data for use by views in an application, and make that data available in a lifecycle-safe fashion, while helping us reduce the need to reload data unnecessarily.

## Encapsulating View State

DevFeed: [Encapsulating View State](<https://devfeed.tech/articles/encapsulating-view-state-30593.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2019/07/encapsulating-view-state/>)

Published: 2019-07-19T16:00:00Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [business logic](<https://devfeed.tech/topics/business-logic.md>), [Testing](<https://devfeed.tech/topics/testing.md>), [App](<https://devfeed.tech/topics/app.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [app](<https://devfeed.tech/tags/app.md>), [app-architecture](<https://devfeed.tech/tags/app-architecture.md>), [business-logic](<https://devfeed.tech/tags/business-logic.md>), [code](<https://devfeed.tech/tags/code.md>), [error](<https://devfeed.tech/tags/error.md>), [livedata](<https://devfeed.tech/tags/livedata.md>), [messages](<https://devfeed.tech/tags/messages.md>), [testing](<https://devfeed.tech/tags/testing.md>), [viewmodel](<https://devfeed.tech/tags/viewmodel.md>)

### AI overview

This article presents an approach to encapsulating view state in Android applications with AndroidX ViewModel. It explains how putting business logic and decision-making code in ViewModels can simplify Activities and Fragments and make the valuable code easier to test.

### Source excerpt

The AndroidX ViewModel has become a central component in many modern Android apps. They offer a relatively simple abstraction to encapsulate business logic and decision making code which separates it from the platform frameworks, which are often difficult to test. Since ViewModels are easier to test than Framework classes like Activity and Fragment due to their simple lifecycles and lack of external dependencies, it makes sense to try to put as much logic, the complex decision-making code that should be tested, in ViewModels as possible. Ideally we'll be left with a view layer, either Activities, Fragments, or something else, which is so simple that it's not worth testing.

## Cloud Build Recipes - Saving the Build Cache

DevFeed: [Cloud Build Recipes - Saving the Build Cache](<https://devfeed.tech/articles/cloud-build-recipes-saving-the-build-cache-30592.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2019/02/cloud-build-recipes-saving-the-build-cache/>)

Published: 2019-02-15T20:13:00Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Caching](<https://devfeed.tech/topics/caching.md>), [Google Cloud Platform (GCP)](<https://devfeed.tech/topics/google-cloud.md>), [ci](<https://devfeed.tech/topics/ci.md>), [Gradle](<https://devfeed.tech/topics/gradle.md>), [Android](<https://devfeed.tech/topics/android.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [caching](<https://devfeed.tech/tags/caching.md>), [cloud](<https://devfeed.tech/tags/cloud.md>), [continuous-integration](<https://devfeed.tech/tags/continuous-integration.md>), [gradle](<https://devfeed.tech/tags/gradle.md>)

### AI overview

This tutorial explains how to speed up Google Cloud Build CI builds by caching dependencies between builds. It describes deploying cache builders, storing cache files in Google Cloud Storage, restoring the cache early in the build process, and choosing cache keys tied to the files that define the cached items.

### Source excerpt

When building development projects on your local workstation speed is important. A quick feedback cycle can mean the difference between a productive day of test driven development and a day filled with distraction. Your continuous integration environment is different. Since CI is responsible for preventing bad code from being merged and creating production releases, most people value precision over speed. That's why most CI environments utilise isolated build environments and always run all of the tests.

## Cloud Build Recipes - Incrementing Build Numbers

DevFeed: [Cloud Build Recipes - Incrementing Build Numbers](<https://devfeed.tech/articles/cloud-build-recipes-incrementing-build-numbers-30591.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2019/01/cloud-build-recipes-incrementing-build-numbers/>)

Published: 2019-02-01T03:00:00Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Google Cloud Platform (GCP)](<https://devfeed.tech/topics/google-cloud.md>), [ci](<https://devfeed.tech/topics/ci.md>), [Android](<https://devfeed.tech/topics/android.md>), [Bash](<https://devfeed.tech/topics/bash.md>), [Environment Variables](<https://devfeed.tech/topics/environment-variables.md>), [Disk image](<https://devfeed.tech/topics/disk-image.md>), [Dockerfile](<https://devfeed.tech/topics/dockerfile.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [bash](<https://devfeed.tech/tags/bash.md>), [ci](<https://devfeed.tech/tags/ci.md>), [cloud](<https://devfeed.tech/tags/cloud.md>), [cloud-storage](<https://devfeed.tech/tags/cloud-storage.md>), [docker](<https://devfeed.tech/tags/docker.md>), [environment-variables](<https://devfeed.tech/tags/environment-variables.md>)

### AI overview

A tutorial explains how to recreate incrementing CI build numbers in Google Cloud Build for Android app versioning. It uses a shared configuration folder, a text file, and Bash scripts to pass the updated number between isolated build steps, with an optional Docker container for deployment.

### Source excerpt

Many CI services use an incrementing build number to identify builds. GCB, however, uses a randomly assigned unique identifier, as is common in large scale systems. While this is common and helps make GCB infinitely scalable, it makes it difficult to reason about the relationship between builds. Since I like to use the CI build number in my Android app versioning, I decided to reimplement this functionality using a simple script and text file stored in a shared location. The idea is simple, each build mounts a shared config folder, reads the version number from a text file, and overwrites that text file with a new version number.

## Google Cloud Build for Android

DevFeed: [Google Cloud Build for Android](<https://devfeed.tech/articles/google-cloud-build-for-android-30590.md>)

Original publisher: [Read original article](<https://ryanharter.com/blog/2018/12/google-cloud-build-for-android/>)

Published: 2018-12-21T20:27:21Z

Content type: tutorial

Language: en

Sources: [Blogs on Ryan Harter](<https://devfeed.tech/sources/blogs-on-ryan-harter.md>)

Topics: [Google Cloud Platform (GCP)](<https://devfeed.tech/topics/google-cloud.md>), [Android](<https://devfeed.tech/topics/android.md>), [CI/CD](<https://devfeed.tech/topics/cicd.md>), [CircleCI](<https://devfeed.tech/topics/circleci.md>), [Docker](<https://devfeed.tech/topics/docker.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [circleci](<https://devfeed.tech/tags/circleci.md>), [continuous-integration](<https://devfeed.tech/tags/continuous-integration.md>), [delivery](<https://devfeed.tech/tags/delivery.md>), [docker](<https://devfeed.tech/tags/docker.md>), [google-cloud](<https://devfeed.tech/tags/google-cloud.md>)

### AI overview

This introductory post examines Google Cloud Build as a continuous integration and delivery option for Android projects. It explains the author's limitations with CircleCI, including container memory constraints and projected costs, and introduces a series on configuring Google Cloud Build for Android.

### Source excerpt

As I've written before, I'm always on the lookout for a great continuous integration and delivery system. For a long time I used CircleCI, but in the last month or so I've started hitting some limitations that I needed to work around. My requirements are pretty standard, I need something that works well with Android, requires minimal maintenance, and can handle somewhat larger projects. After a souring experience with CircleCI, and a short search for other hosted providers, I stumbled upon an early solution from Google that intrigued me.

[Next page](<https://devfeed.tech/sources/blogs-on-ryan-harter.md?cursor=WyIyMDE4LTEyLTIxVDIwOjI3OjIxKzAwOjAwIiwgImUzOWRmZTAzLTVmNWMtNDU5ZC1hN2Q0LTViMzhkMjY2ZjRiYSJd>)