Skip to content

Commit

Permalink
Release post-18
Browse files Browse the repository at this point in the history
  • Loading branch information
DandyLyons committed Oct 2, 2024
1 parent 492c1dc commit 95af364
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 1 deletion.
76 changes: 76 additions & 0 deletions content/en/posts/post-18/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
date: 2024-10-01
title: "The Illusion of Simplicity: Understanding Complexity and Abstractions"
slug: the-illusion-of-simplicity
images: [""]
description:
topics: ["Software Development", "Abstractions"]
---
# "The Illusion of Simplicity: Understanding Complexity and Abstractions"
I'm starting to see a pattern that seems to replay again and again. A shiny new technology comes out that solves a problem. A bunch of developers flock to it, evangelizing it to everyone else. Eventually the tech disappoints. The developers complain that the new solution is so complicated, and they long for the next shiny new thing.

Surely this pattern has existed for a long time, and will continue to repeat. But why does it happen? I think it's because we have fallen for a fallacy. The fallacy is this:

> X technology will make things simpler.
It won't. It doesn't. That complexity still exists. The only difference is you are not handling it anymore. Now that technology is handling it. **The complexity is not eliminated. The complexity is delegated.**

The word for this is *abstraction*.

## What Are Abstractions?
How do you store a list of values? You use an array. That's an abstraction.<br>
How do you implement an array? You could use a linked list. That's another abstraction. That's an abstraction.<br>
How do you implement a linked list? Well you're going to need pointers.<br>
How do you implement pointers? Well a pointer is just a number pointing to a position in memory?

As we can see above, abstractions do not eliminate complexity. They don't even reduce complexity. In fact, **abstractions actually always increase complexity**.

## Why Do We Use Abstractions?
So why do we use abstractions then? Because abstractions **delegate** complexity. This is what makes abstractions so powerful.

If you want to store a list of values, you just use an array. You don't need to think about linked lists. You can just use the array type provided to you by a library. You have delegated away that complexity to something else. This now frees you to tackle larger, more complex problems.

## The Problem With Abstractions
But what happens if the problem isn't really solved? Now you have to solve the problem. Except now the problem is bigger and more complex than it was before.

Now you have to handle the complexity of the original problem **plus** the extra complexity of the abstraction!

Each abstraction can fail in at least two ways:
1. The abstraction can be broken, not fulfilling what it promised to do.
2. The abstraction can "leak", meaning it doesn't actually hide the complexity.

Even worse, the [Law of Leaky Abstractions](https://www.laws-of-software.com//laws/leaky-astractions/) essentially says that every abstraction will leak.

>All non-trivial abstractions, to some degree, are leaky.
>
>-- Joel Spolsky, 2002
## Abstractions: Can't Live With 'em, Can't Live Without 'em
Well that settles it. Let's get rid of all abstractions. Keep things simple.

If that's your attitude, then good luck.

No one truly gets rid of all abstractions. Even by using a programming language you are already using an abstraction. You're not writing assembly. You're certainly not writing machine code.

If you want to solve big problems, then that requires big complexity. And if you want to handle that complexity, you're going to need to delegate some of it with abstractions.

## The Path Forward

Understanding these principles doesn't mean we should avoid abstractions or new technologies. Instead, it calls for a more nuanced approach:

1. **Thoughtful Adoption**: Carefully evaluate new tools and abstractions. Consider their long-term implications, not just short-term gains. Be aware that adding layers of abstraction may make individual components simpler, but always increase overall system complexity.
2. **Deep Understanding**: Strive to understand the layers beneath your abstractions. This knowledge is invaluable when abstractions leak. When[^1] issues arise, be prepared to dive into the layers of abstraction to identify root causes.
3. **Balanced Approach**: Use abstractions to manage complexity, but be prepared to handle the complexity they can't fully hide.
4. **Continuous Learning**: Stay curious about both high-level abstractions and low-level details in your field. Recognize that mastering a new abstraction is an investment. It may slow you down initially before it speeds you up.

[^1]: not if

## Escaping Abstraction Hell
So has your heart been broken by yet another framework with broken promises? Fret not. It's all a part of the process. Find another way to manage that complexity. Consider these options:
1. **Eliminate**: Perhaps you don't need this abstraction. Consider removing it.
2. **Delegate**: Perhaps you should replace your abstraction with a better fit.
3. **Incorporate**: Perhaps your should add a new abstraction alongside your current abstractions.

## Conclusion
Don't be afraid of complexity. Embrace it. Find the right abstractions that you are comfortable using. You can handle it.

82 changes: 82 additions & 0 deletions content/en/unlisted/Differentiating Parameterized Tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
date: 2024-10-01
title: Differentiating Parameterized Tests in Swift Testing
slug: differentiating-parameterized-tests-in-swift-testing
images: [""]
description:
topics: ["Swift Testing"]
---
# Differentiating Parameterized Tests in Swift Testing
Swift Testing is a fantastic addition to the Swift ecosystem. They are concise and easy to understand. Parameterized tests are one of the best features of Swift Testing, but sometimes the framework needs a bit more information in order to work properly. Let's dig in.

## What Are Parameterized Tests?
Parameterized tests are a feature that makes it easy to reuse testing logic across multiple cases of data. To see why that would be valuable let's look at Swift Testing's predecessor, XCTest.

### What's the Problem?
XCTest and other testing frameworks make it difficult to reuse testing code. Because of this, many test suites are filled with tests that are essentially identical. This increases the burden of maintaining and updating tests.

```swift
import XCTest

func validateEmail(_ string: String) -> Bool {
// Check if the string is empty or too short to be a valid email
guard string.count >= 3 else { return false }

// Check if '@' exists and is not at the beginning or end
guard let atIndex = string.firstIndex(of: "@"),
atIndex != string.startIndex,
atIndex != string.index(before: string.endIndex) else {
return false
}

// Split the string into parts before and after '@'
let beforeAt = string[..<atIndex]
let afterAt = string[string.index(after: atIndex)...]

// Check if there are characters before '@' and a domain after '@'
return !beforeAt.isEmpty && afterAt.contains(".")
}

class EmailValidationTests: XCTestCase {
func testValidate_helloEmail() {
let input = "[email protected]"
XCTAssertTrue(validateEmail(input))
}

func testValidate_nameDotEmail() {
let input = "[email protected]"
XCTAssertTrue(validateEmail(input))
}
}
```

In the tests above, there is virtually no difference in the logic between `testValidate_helloEmail()` and `testValidate_nameDotEmail()`. The only difference is the string that is being tested. We want to be able to test many different strings in order to test various edge cases, but each new string adds a significant amount of boilerplate code that needs to be maintained.

Another option would be to combine multiple test cases into one like this.
```swift
class EmailValidationTests: XCTestCase {
func testValidateEmail() {
let inputs = ["[email protected]", "[email protected]", "DandyLyons"]
for string in inputs {
XCTAssertTrue(validateEmail(string))
}
}
}
```
However this approach isn't great either because when a test fails, we don't know which string caused it to fail. In the code above, the first two strings pass, but the third string causes the entire test to fail, and we don't know which string caused the failure. To be fair, XCTest offers many other more robust ways to solve this problem, but Swift Testing has a solution that is very concise and ergonomic: **parameterized tests**.

### What's the Solution?
In Swift Testing, we can define tests that accept parameters. We

## Differentiating
### Differentiating Through `Equatable`

### Differentiating Through `Identifiable`

### Other Ways to Differentiate Tests

### `CustomTestArgumentEncodable`


---
## Further Exploration
2 changes: 1 addition & 1 deletion content/en/unlisted/post-ideas.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ USE https://blogrecorder.com/
- [x] Typing keyboard shortcuts.
- [ ] Swift/C++ interoperability
- [x] Helpful features of Swift Package Index
- [ ] How to test in TCA even without Equatable, using xctassertnodifference
- [ ] How to test in TCA even without Equatable, using expectnodifference
- [ ] AI Architecture
- [No Priors: Tengyu Ma | June 6th](https://share.snipd.com/episode/8db8d89c-b912-4aa0-8f7f-d2d712947c8f)
- [ ] Dependabot for Swift packages: https://github.blog/changelog/2023-08-01-swift-support-for-dependabot-updates/
Expand Down

0 comments on commit 95af364

Please sign in to comment.