corey haines

author & creator of coderetreat
+ Follow
since Nov 19, 2018
Merit badge: grant badges
For More
Cows and Likes
Cows
Total received
6
In last 30 days
0
Total given
0
Likes
Total received
4
Received in last 30 days
0
Total given
0
Given in last 30 days
0
Forums and Threads
Scavenger Hunt
expand Ranch Hand Scavenger Hunt
expand Greenhorn Scavenger Hunt

Recent posts by corey haines

I've always thought that the transition from "test-first programming" in the late 1990s to "test-driven development" was influenced by the same kind of forces that moved us from "janitor" to "property custodian".




I think there is no real consensus on the "meaning" of the terms. Both test-first and test-driven involve writing tests first. However, I have found it useful to think of the difference revolving around what your reaction is when you find code that is hard to test.

test-first: this code is hard to test, let me see make some changes to the test to enable testing the code.

test-driven: this code is hard to test, let me make some changes to the code to enable testing the code.

Why do I make this distinction? I find it easier to introduce people to the concept when you can move through a progression of learning techniques:

- Write all your code, write all your tests
- Write some code, write tests for that code
- Write a tiny bit of code, write tests for that code
- Write code and tests simultaneously
- Write test first, then write code
- Write test first, design code based on making those tests easy to write

It also helps differentiate between "test-first"  being about verification and "test-driven" being about design. Changing your test to enable you to test the code is about verifying that the code works. Changing your code to make it easy to test is about adjusting your design because of the need for the test.

I've found making an explicit distinction like this helps in conversation, so we know what we are talking about.

As I said at the beginning, I would easily wager a lot of money that you could find someone readily who disagrees with me on these definitions and uses their own.


5 years ago
I also prefer to see an actual infinite, unbounded grid, not a torus (infinite, bounded) like you have here.
5 years ago
The rules are always an interesting question of how to implement them.

So, looking here
https://github.com/liutaurasvilda/conways-gol/blob/master/core-lib/src/main/java/io/github/liutaurasvilda/gol/Cell.java#L7-L9

There are a couple things that make me go hmmmm:

`next` is a rather vague name for the function. Is there a better, more intention-revealing name for what this is doing?

Then, we have this
https://github.com/liutaurasvilda/conways-gol/tree/master/core-lib/src/main/java/io/github/liutaurasvilda/gol#L8-L9


There are these magic numbers here. Why 2 and 3? Why the or?

Is there an intention-revealing name that can be put here? What does the explanation say about the 2 and the 3? Well, if there are less than 2, then it dies as if by underpopulation, greater than 3 by overpopulation. 2 or 3 feels like sort of "just right population" or something. Could the meaning of the rules be encoded somehow? Maybe something like



The `this` in there is also a bit confusing to me. Why DEAD or this? Isn't it really DEAD or ALIVE? The code might be more clear if it was


It seems as though the purpose of `Cell` itself is just to store the rule. Perhaps the name of `Cell` would be more clear if it was `EvolutionRules` or some such like that?
5 years ago
One thing I always look for initially is duplication of the knowledge around the topology.

So, you have `World` which knows that it is a 2-d topology. For example
https://github.com/liutaurasvilda/conways-gol/blob/master/core-lib/src/main/java/io/github/liutaurasvilda/gol/World.java#L67-L71
and
https://github.com/liutaurasvilda/conways-gol/blob/master/core-lib/src/main/java/io/github/liutaurasvilda/gol/World.java#L48-L53

But you also have a `Location` which knows it.
https://github.com/liutaurasvilda/conways-gol/blob/master/core-lib/src/main/java/io/github/liutaurasvilda/gol/Location.java

That puts a limitation on ease of change.

I'd isolate that somewhere.
5 years ago

Junilu Lacar wrote:How well it supports the current requirements and how well it adapts to new requirements as they come?



I would definitely agree with this.

I love the saying

You are never as ignorant as you are right now


so I like to work on satisfying the current requirements, but working in a way (I've found good naming + eliminating duplication to generally be enough) to help with making an adaptable system when I learn more about what is needed.

But, there is another aspect to this. Often we say that the difference between design and architecture is that the architecture constitutes the parts that are hard to change.
In this context, then I think it is important to think about how extensible the architecture is. Are their natural seams for adding behaviour without requiring a change? But, then, of course, the inevitable problem of deciding which seams are important.
5 years ago

Harry Kar wrote:Hi Corey,
The ancient Roman architect Vitruvius considered the question:

what makes a good building?


In his treatise De Architectura Libri Dece (Ten Books on Architecture), written in 29 B.C., he proposed three principles that many think provide a good starting point for
evaluating software designs.

A well-designed building, said Vitruvius, should have the qualities of commodity, firmness, and delight.


Based on your experience what you think about those considerations and what is  the best way to meet commodity, firmness, and delight qualities in a well balanced manner?

TIA
Harry



Thanks for this great question, Harry.

My answer for "what is the best way to X" is almost always "understand the people who will be using your system." Or, in other words, "communication and empathy."

In your message, you talk about the different roles and different goals as though they are happening in a vacuum. If they are, then I think there will always be problems in balancing them. Some people will want one thing, others will want to emphasize another. But, if you bring everyone together and truly understand WHY you are building what you are building, then it is more likely that you can achieve the right balance of these three qualities.

For example, it may turn out that you may not need to emphasize delight at the beginning. Or perhaps, at a certain stage, you maybe not need it to be you want it be as fast and small as possible.
As a concrete situation, I'm currently working on fixing a rather tough bug. There is the "correct" way to fix it, but we are going to go with the way that is quickest to implement in order to get it into production. Then, we may come back and adjust it to be faster / smaller. We may not, though.

Whenever there is a concept of "as possible," we run into problems. Instead, I would say that we want to balance these three qualities to "just as much as needed," which means we need to start with understanding what is needed.

Hope this makes sense.
5 years ago

meenakshi sundar wrote:
Dear Author,

On theory, Design drives implementation, but in the real world  I have seen on many occasions a good design implemented very badly

How do we manage to narrow the gap between A good design vs bad implementation?

Thanks
Sundar



Junilu makes a good point on this.

Are you referring to the act of first designing, then putting an implementation together that doesn't satisfy or abide by the design.

In the past, and I know one very specific example in my career, where we built a design for our system, then moved into the "implementation phase" when we almost immediately realized that the design wasn't going to work. So, we had to change our implementation, which made it diverge from the design.


What would constitute a "bad implementation?"


The way that I try to keep from having a divergent value in my design vs my implementation is to minimize the time and size between making the design and making the implementation. This is often part of the idea of iterative- or incremental design. In general, pick a small set of behaviours that are needed and implement a design to satisfy those. Then, determine another behaviour and see if your implementation will satisfy that. If not, then see what changes to your design are needed. make those changes, implement them, then verify that the original behaviours are still satisfied (a nice automated regression test suite is good for this). Then, pick the next small behaviour and design/implement a solution for that. Then just keep doing this.

During this process, it may become clear that the path you are on is a dead-end and won't satisfy upcoming behaviours. At this point, you have A LOT of knowledge about what the design needs to support, a lot of knowledge about how to build a system that will satisfy them. At this point, you can either make changes to the existing design or build a new design that doesn't make the same mistakes that led you to the dead-end.

There is a saying "you are never more ignorant than you are right now," so taking small steps in building and implementing a design helps fill in the gaps in knowledge.


5 years ago
All that being said, I do think that "better design" comes from two things:

- Understand your context, especially around the need for future change
- Apply the 4 rules of simple design (specifically naming and duplication elimination) within the context that need for change.
5 years ago
I think an important question to ask is

"What is a good design principle?"
or perhaps
"What is a good design?"

Before we can ask about why we can't formalize them or why don't we see them happening more often. This can be an interesting discussion, but it is often context-specific and hard to get to "good design." Depending on the situation and the person, I think you'll often get differing opinions.

I like the idea of "better design" that takes into account relative benefits of different designs based on the context.

As an example, if it easier to rewrite something than to maintain it, then the definition of "quality" is suddenly changed.

Even something as simple as "no duplication" can have varying levels of adherence and return on investment. Even the definition of "duplication" can be argued .

I know this is sort of a non-answer, but I know that, in my past, I've fallen into conversations about "good design" that bordered on harmful for certain situations.
5 years ago

Burk Hufnagel wrote:

corey haines wrote:Hi!

I think a lot of that depends on the constraints and people's familiarity with TDD. Most people that I ran into came to a coderetreat with the expectation that it was "a chance to practice or learn TDD," so they at least tried. Personally I always emphasized that a coderetreat is a great time to try new things, since the goal isn't to finish the implementation, nor even to get anywhere at all. If a single test is written, but it is a great test, then that session should be considered successful.

-Corey



Thank you for the response. I was hoping to leverage your experience and get a feel for TDD adoption around the globe. Based on what I've seen in Atlanta, GA, USA, it seems that TDD still isn't common (less than 50% use it) and that many senior developers don't want to try it because... fill in the blank -- while younger developers at least seem open to the idea, give it a shot and mostly find it valuable.



Honestly I don't think the adoption is that high anywhere. It can be easy to get into a bubble and start to think that it is more common than it actually is.

I do believe that automated testing has been ever-increasing, then to a slightly lesser extent having developers work on the automated tests. Writing the tests first, which is a natural step towards, TDD, can be counter-intuitive and hard to find the time to learn.

My general approach in the past with people who were resistant was to ask how they currently verified their code (focus on the test part, rather than the design part), both at the time of writing and in regression. Most developers I've met have some way they do it. Then, we can talk about some of the benefits of a test-first/test-driven methodology and see if there are differences. I've known some folks in the past who did just fine without it, and the effort of switching to use this technique wouldn't have really been that valuable to them. The easiest and biggest benefit comes from folks who don't have an existing way to do it.
5 years ago

Burk Hufnagel wrote:

Liutauras Vilda wrote:I guess quite usual answer is when current implementation no longer works with new requirements. So really there is no quality evolution with an existing functionality. Which is of course sad.


But if the code no longer meets the requirements that means the new version behaves differently than the original code, so the change doesn't qualify as a refactoring. Right?



This goes back to the old quote (i think it was either Kent Beck or Ward Cunningham) about "if you have a hard change coming up, refactor the code until the hard change becomes easy (note: this may be hard), then make the easy change."

For me there are (at least) two levels of refactoring:
- General cleanup of code directly after having written it. This involves abiding closely by the 4 rules of simple design, especially naming and duplication. After writing some code, look to see if you have any small, easy changes to make to clean things up.
- Pre-feature adjustement. This is what the quote kind of refers to. When you have a new feature, one that you either didn't anticipate or just didn't worry about at the time the code was written, it can be helpful to now look at your design and see if it is welcoming to the new feature. If it isn't, perhaps there are some changes that can be made that will make it more accepting. If so, then make those. Don't change the behaviour, just alter the design to make the new feature more easy to implement. Then implement it.

5 years ago

Burk Hufnagel wrote:Corey,
I'm also wondering if you've noticed any correlation between the use of TDD and the quality of the code people produce, or whether they manage to get the game working at the end of the allotted time.

Thanks,
Burk



In the context of coderetreat sessions, I don't think I've really seen that much of a correlation. A lot of the time, when I was facilitating, I would emphasize putting a lot of thought into the quality of the tests and how they can influence the design, rather than explicitly working on writing a lot of code, especially enough to see an impact.

one thing I notice a lot is that people often tend to be more on the "test-first" side of coding, rather than the "test-driven" side, meaning that they are writing tests to verify the design they want to implement, rather than writing a test to outline or specify some behaviour and then really truly write just enough code to get it passing, followed by a heavy refactoring stage.

This is often shown by a jump towards the "how I am going to implement a grid" discussion. Rather than talking about the qualities of the grid under evolution.
5 years ago
Hi, Folks!

I'm looking forward to chatting a bit!
5 years ago

Burk Hufnagel wrote:Corey,
In the excerpt, there are several examples of test people write while working on getting their programs to work. Based on what you've seen, do most of the participants follow some form of TDD? Have you seen a difference between less experienced developers and more experienced developers regarding TDD usage?

Thanks,
Burk



Hi!

I think a lot of that depends on the constraints and people's familiarity with TDD. Most people that I ran into came to a coderetreat with the expectation that it was "a chance to practice or learn TDD," so they at least tried. Personally I always emphasized that a coderetreat is a great time to try new things, since the goal isn't to finish the implementation, nor even to get anywhere at all. If a single test is written, but it is a great test, then that session should be considered successful.

-Corey
5 years ago