• No sense of the depth of the discipline of software engineering
  • Shallow knowledge but deep overconfidence
  • Wastes energy in disputes about the cosmetics of software engineering

irrelevant, I'm already an expert

Photo by Aleksey Boev on Unsplash

Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account and AddressParser.

 

Avoid words like Manager, Processor, Data, or Info in the name of a class.

🤔

/**
 * Class summarizing user interest data
 */
data class UserData(
    val bookmarkedNewsResources: Set<String>,
    val viewedNewsResources: Set<String>,
    val followedTopics: Set<String>,
    val themeBrand: ThemeBrand,
    val darkThemeConfig: DarkThemeConfig,
    val useDynamicColor: Boolean,
    val shouldHideOnboarding: Boolean,
)
data class AmpLinkInfo(
	val ampLink: String, 
    val destinationUrl: String? = null,
)

Why not use tuples?

package com.squareup.moshi.kotlin.codegen.ksp

private class JsonClassSymbolProcessor(
  environment: SymbolProcessorEnvironment,
) : SymbolProcessor {


  override fun process(resolver: Resolver): List<KSAnnotated> {
    val generatedAnnotation = generatedOption?.let {
      AnnotationSpec.builder(ClassName.bestGuess(it))
        .addMember("value = [%S]", JsonClassSymbolProcessor::class.java.canonicalName)
        .addMember("comments = %S", "https://github.com/square/moshi")
        .build()
    }
}    
internal class OfflineFirstTopicsRepository @Inject constructor(
    private val topicDao: TopicDao,
    private val network: NiaNetworkDataSource,
) : TopicsRepository {
internal class OfflineFirstNewsRepository @Inject constructor(
    private val niaPreferencesDataSource: NiaPreferencesDataSource,
    private val newsResourceDao: NewsResourceDao,
    private val topicDao: TopicDao,
    private val network: NiaNetworkDataSource,
    private val notifier: Notifier,
) : NewsRepository {
internal class OfflineFirstTopicsManager @Inject constructor(
    private val topicDao: TopicDao,
    private val network: NiaNetworkDataSource,
) : TopicsRepository {
internal class OfflineFirstNewsManager @Inject constructor(
    private val niaPreferencesDataSource: NiaPreferencesDataSource,
    private val newsResourceDao: NewsResourceDao,
    private val topicDao: TopicDao,
    private val network: NiaNetworkDataSource,
    private val notifier: Notifier,
) : NewsRepository {
  ForbiddenClassName:
    active: true
    forbiddenName: [".*Manager"]
class ForbiddenNameDetector : Detector(), SourceCodeScanner {

    override fun getApplicableUastTypes(): List<Class<out UElement>> {
        return listOf(UClass::class.java)
    }

    override fun createUastHandler(context: JavaContext): UElementHandler? {
        if (isKotlin(context.psiFile?.language)) return null

        return object : UElementHandler() {
            override fun visitClass(node: UClass) {
                val matches = forbidden.filter { it.pattern.matches(node.name) }

                for (match in matches) {
                    context.report(
                        ISSUE,
                        context.getLocation(node),
                        "Name ${node.name} is forbidden for ${match.reason}"
                    )
                }
            }
        }
    }
Executors.newSingleThreadExecutor().asCoroutineDispatcher()
// User.kt

data class User(
	val firstName: String,
    val lastName: String,
    val id: Long
)
// Users.kt

fun User.toLoggableString() = TODO()
// Gold.kt

data class Gold(
	val amount: Int,
)
// Golds.kt

fun Gold.toSilver() = amount * 1000
// FauxPas.kt

data class FauxPas(
	val context: String,
    val action: String
)

/** A spec or type which can be annotated. */
public interface Annotatable {
	public val annotations: List<AnnotationSpec>
}
/** A spec which contains documentation. */
public interface Documentable {
	public val kdoc: CodeBlock
}  

What about something that can have a TypeSpec?

  get the garbage bag from under the sink
  carry it out to the garage
  dump it in the garbage can
 

fun takeOutGarbage() {
    RetrievalStrategyFactory.create(Strategy.WITH_HANDS).retrieve(garbageBag)
    Carrier.of(Hand.RIGHT).carryTo(garbageCan)
    Dumper.dumpIn(garbageCan)
}
@Composable
private fun SearchTextField(
    searchQuery: String,
    onSearchQueryChanged: (String) -> Unit,
    onSearchTriggered: (String) -> Unit,
) {
    val focusRequester = remember { FocusRequester() }
    val keyboardController = LocalSoftwareKeyboardController.current

    TextField(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
            .focusRequester(focusRequester)
            .onKeyEvent {
                if (it.key == Key.Enter) {
                    onSearchExplicitlyTriggered()
                    true
                } else {
                    false
                }
            }
            .testTag("searchTextField"),
        shape = RoundedCornerShape(32.dp),
        value = searchQuery,
        keyboardOptions = KeyboardOptions(
            imeAction = ImeAction.Search,
        ),
        keyboardActions = KeyboardActions(
            onSearch = {
                onSearchTriggered()
            },
        ),
        maxLines = 1,
        singleLine = true,
    )
    LaunchedEffect(Unit) {
        focusRequester.requestFocus()
    }
}

obtain

fetch

retrieve

acquire

calculate

generate

There are no solutions, only trade-offs

Thomas Sowell

Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account and AddressParser.

Avoid words like Manager, Processor, Data, or Info in the name of a class.

Getting a good instinct for names is hard. You will have to learn to walk the tightrope between obscurity and verbosity.

Follow the idiomatic names in your language and open source community. For team projects, enforce naming conventions using static analysis to avoid bike-shedding.

At the end of the day, decisions on names are compromises too, so learn to deal with names that aren't perfect.

Photo by Drew Hays on Unsplash

Debit

Credit

$10,000

$10,000

class PaymentProcessor(private val creditCardServer: CreditCardServer) {

    fun processPayment(creditCard: CreditCard, money: Money) {
        if (!creditCardServer.isAvailable()) {
            throw NotAvailableException()
        }

        val transaction = creditCardServer.beginTransaction().transaction
        val payment = creditCardServer.pay(transaction, creditCard, 500)

        if (payment.isOverMaxBalance()) {
            throw OverMaxBalanceException()
        }
    }
}
fun testCreditCardIsCharged() {
    val paymentProcessor = PaymentProcessor(mockCreditCardServer)
    whenever(mockCreditCardServer.isServerAvailable()).thenReturn(true)
    whenever(mockCreditCardServer.beginTransaction()).thenReturn(mockTransactionManager)
    whenever(mockTransactionManager.getTransaction()).thenReturn(transaction)
    whenever(mockCreditCardServer.pay(transaction, creditCard, 500)).thenReturn(mockPayment)
    whenever(mockPayment.isOverMaxBalance()).thenReturn(false)
    
    paymentProcessor.processPayment(creditCard, Money.dollars(500))
    
    verify(mockCreditCardServer).pay(transaction, creditCard, 500)
}
class HappyBirthdayViewModel(
    private val userRepository: UserRepository
    private val clock: Clock
) {
    init {
        val birthday = userRepository.user.getField("brthiday")
        if (birthday == clock.today()) {
            _viewState = _viewState.copy(party = true)
        }
    }
}
fun testBirthday() {
    val user = mock()
    whenever(userRepository.user).thenReturn(user)
    val viewModel = HappyBirthdayViewModel(userRepository)
    verify(user).getField("birthday")
}
fun testCreditCardIsCharged() {
    val paymentProcessor = PaymentProcessor(mockCreditCardServer)
    whenever(mockCreditCardServer.isServerAvailable()).thenReturn(true)
    whenever(mockCreditCardServer.beginTransaction()).thenReturn(mockTransactionManager)
    whenever(mockTransactionManager.getTransaction()).thenReturn(transaction)
    whenever(mockCreditCardServer.pay(transaction, creditCard, 500)).thenReturn(mockPayment)
    whenever(mockPayment.isOverMaxBalance()).thenReturn(false)
    
    paymentProcessor.processPayment(creditCard, Money.dollars(500))
    
    verify(mockCreditCardServer).pay(transaction, creditCard, 500)
}
fun testCreditCardIsCharged() {
	paymentProcessor = PaymentProcessor(fakeCreditCardServer)
	paymentProcessor.processPayment(creditCard, Money.dollars(500))
	assertEquals(500, fakeCreditCardServer.getMostRecentCharge(creditCard))
}

replace just this with an in-memory database

applicability

fidelity

brittle

complex

Photo by Markus Spiske on Unsplash

Premature optimization is the root of all evil

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

 

Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified

Structured Programming with go to Statements, ACM Journal Computing Surveys, Vol 6, No. 4, Dec. 1974

Photo by Edho Pratama on Unsplash

Dangerous minds: How we're getting developer education wrong

By David Rawson

Dangerous minds: How we're getting developer education wrong

  • 42