본문 바로가기

Programming/SW Engineering , Architecture, etc.

Agile Software Engineering (You aren't gonna need it, Yagni) 소프트웨어 개발에서 고려되어야 할 부분 (야그니), agile, 에자일

Origin ( Here )

원본 ( 여기 )

 

 

 불필요한 개발을 늘리지 말자. 당장 필요한 메소드, 속성이 아니면 심플하게~

 

 

 Yagni originally is an acronym that stands for "You Aren't Gonna Need It". It is a mantra from ExtremeProgramming that's often used generally in agile software teams. It's a statement that some capability we presume our software needs in the future should not be built now because "you aren't gonna need it".

 

 Yagni (이하 야그니) 는 "You Aren't Gonna Need it" (넌 이거 필요없을 것이여) 의 약자이다. 이것은 익스트림 프로그래밍에서 나왔는데, 종종 에자일 소프트웨어 개발팀에서 쓰이곤 했다. 이건 우리의 소프트웨어가 나중에 필요로 할 것이라고 예상되는 캐파가 지금 빌드되어 들어가서는 안된다는, 왜냐면 필요 없을 것이니까. 라는 말이다.

 

 Yagni is a way to refer to the XP practice of Simple Design (from the first edition of The White Book, the second edition refers to the related notion of "incremental design"). [1] Like many elements of XP, it's a sharp contrast to elements of the widely held principles of software engineering in the late 90s. At that time there was a big push for careful up-front planning of software development.

 

 야그니는 심플디자인 등 여러 책에서 참조된 방법이다. XP 의 다른 많은 것들 처럼, 90년대의 소프트웨어 엔지니어링 원리로 널리 퍼진 내용들과는 첨예한 대조를 이루고 있다. 그 당시에는, 소프트웨어 개발 이후에 대한 세심한 계획 수립이 필요 했다.

 

 Let's imagine I'm working with a startup in Minas Tirith selling insurance for the shipping business. Their software system is broken into two main components: one for pricing, and one for sales. The dependencies are such that they can't usefully build sales software until the relevant pricing software is completed.

 

 배달업체에 보험을 파는 스타트업에서 일한다고 가정해보자. 그들의 소프트웨어 시스템은 두개의 메인 컴포넌트로 분리된다. 한개는 pricing, 한개는 sales 이다. 이 디펜던시들은, 관련된 가격관련 소프트웨어가 끝날 때까지 세일즈 관련 소프트웨어가 제대로 만들어 질 수 없게 되어있다.

 

 At the moment, the team is working on updating the pricing component to add support for risks from storms. They know that in six months time, they will need to also support pricing for piracy risks. Since they are currently working on the pricing engine they consider building the presumptive feature [2] for piracy pricing now, since that way the pricing service will be complete before they start working on the sales software.

 

 이 때에, 해당 팀은 pricing 컴포넌트를 업데이트하여 나중에 폭풍우로 야기될 리스크를 서포트 하고자 한다. 나중의 리스크는 6개월 내에 올 것이라는 것을 알고, 그들은 또한 해적질 당할 리스크로부터 pricing 서포트가 필요할 것이다. 그들은 지금 해적질관한 추정되는 기능들을 만드는 것을 고려한 pricing engine 에 일을 하고 있고, pricing 서비스는 sales 소프트웨어를 시작하기 전에 완료될 것이다.

 

 Yagni argues against this, it says that since you won't need piracy pricing for six months you shouldn't build it until it's necessary. So if you think it will take two months to build this software, then you shouldn't start for another four months (neglecting any buffer time for schedule risk and updating the sales component).

 

 야그니는 이러한 것에 반박하는데, 해적 관련 문제는 6개월간 필요가 없을 것이기 때문에 필요할 때까지 만들 필요가 없다. 만약 이 소프트웨어를 만드는데에 2개월이 걸린다면, 나머지 4개월 동안 다른 것들을 시작하지 말아야 한다 (리스크 스케줄의 버퍼 시간을 무시하고, sales 컴포넌트를 업데이팅 하는 것이다)

 

 The first argument for yagni is that while we may now think we need this presumptive feature, it's likely that we will be wrong. After all the context of agile methods is an acceptance that we welcome changing requirements. A plan-driven requirements guru might counter argue that this is because we didn't do a good-enough job of our requirements analysis, we should have put more time and effort into it. I counter that by pointing out how difficult and costly it is to figure out your needs in advance, but even if you can, you can still be blind-sided when the Gondor Navy wipes out the pirates, thus undermining the entire business model.

In this case, there's an obvious cost of the presumptive feature - the cost of build: all the effort spent on analyzing, programming, and testing this now useless feature.

 

 첫번째 내용에서 야그니를 적용해보면, 우리가 필요할 것으로 생각되는 기능이지만, 우리가 틀릴 수도 있다고 생각이 든다. 결국 에자일 방식의 맥락은 우리가 변화하는 요구를 수용하는 것이다. 계획지향적인 요구사항의 가이드는 반대되는 주장일 수 있는데, 왜냐면 우리는 요구사항의 분석을 제대로 안했기 때문이고, 더 많은 시간과 노력을 넣어야 했다는 것이다. 나는 사람들의 요구를 즉시 알아내는 것이 얼마나 힘들고 소모적인지를 지적하는 것으로 반박하는데, 만약 바로 포착 할 수 있다고 하더라도, 여전히 불확실한 것이 있을 수 있고, 전체적인 비지니스 모델을 약화시키게 된다.

 이러한 경우에, 예상되는 기능의 분명한 비용이 있다 - 개발 비용 : 분석에 쓰이는 모든 노력, 프로그래밍, 그리고 지금은 쓸모없는 이 기능에 대한 테스팅.

 

 But let's consider that we were completely correct with our understanding of our needs, and the Gondor Navy didn't wipe out the pirates. Even in this happy case, building the presumptive feature incurs two serious costs. The first cost is the cost of delayed value. By expending our effort on the piracy pricing software we didn't build some other feature. If we'd instead put our energy into building the sales software for weather risks, we could have put a full storm risks feature into production and be generating revenue two months earlier. This cost of delaydue to the presumptive feature is two months revenue from storm insurance.

 

 하지만 우리가 필요로 하게될 것에 대한 이해가 완벽히 맞다고 가정해보자. 이런 해피한 상황에서도, 필요가 예상되는 기능을 만드는 것은 두가지 심각한 비용을 초래한다. 첫번째는 지연되는 가치의 비용이다. 우리가 해적관련 pricing 소프트웨어에 대한 노력을 쓰면서 우리는 다른 기능들을 만들 수 없게 된다. 만약 대신에 우리가 sales 소프트웨어를 만들어 날씨의 리스크에 대하여 힘을 쏟는다면, 우린 폭풍의 위험에 대한 기능을 제품에 집어넣고, 두달 먼저 이윤을 창출해낼 수 있었을 것이다. 이러한 추정되는 기능 때문에 지연되는 비용은 폭풍에 대한 보험으로부터의 두달간 수익이다.

 

 The common reason why people build presumptive features is because they think it will be cheaper to build it now rather than build it later. But that cost comparison has to be made at least against the cost of delay, preferably factoring in the probability that you're building an unnecessary feature, for which your odds are at least ⅔. [3]

 Often people don't think through the comparative cost of building now to building later. One approach I use when mentoring developers in this situation is to ask them to imagine the refactoring they would have to do later to introduce the capability when it's needed. Often that thought experiment is enough to convince them that it won't be significantly more expensive to add it later. Another result from such an imagining is to add something that's easy to do now, adds minimal complexity, yet significantly reduces the later cost. Using lookup tables for error messages rather than inline literals are an example that are simple yet make later translations easier to support.

 

 흔히 사람들이 나중에 필요할 것으로 추정되는 기능을 만드는 이유는 나중에 만드는 것보다 지금 만드는 비용이 더 작을 것이라고 생각하기 때문이다. 하지만 비용 비교는 적어도 지연되는 비용을 확인해야하고, 불필요한 기능을 만들게될 확률을 적당히 나눠봐야하는데, 2/3 이상이어야 한다.

 종종 사람들은 지금과 나중의 비교적인 비용을 생각하지 않는다. 이러한 상황에서 개발자들을 멘토링해주는 나의 방법 중 하나는 그들에게 묻는 것인데, 나중에 그 기능이 필요할 때 캐파를 넓히려고 리팩토링 하는 것을 상상해보라 하는 것이다. 이 생각을 하는 것은 나중에 추가하는 것이 특별히 많이 비싼 비용이 들지 않을 것이라는 것을 설득하기 충분하기도 한다. 다른 상상의 결과는, 지금 추가하는 것이 쉽다는 것, 최소의 복잡도로 추가하는 것, 하지만 나중의 비용을 확실히 줄이는 것이다. inline literals 보다는 테이블의 에러 메시지를 보는 것들이 간단하지만 나중에 더 쉬운 서포트를 하게 만드는 예이다.

 

 The cost of delay is one cost that a successful presumptive feature imposes, but another is the cost of carry. The code for the presumptive feature adds some complexity to the software, this complexity makes it harder to modify and debug that software, thus increasing the cost of other features. The extra complexity from having the piracy-pricing feature in the software might add a couple of weeks to how long it takes to build the storm insurance sales component. That two weeks hits two ways: the additional cost to build the feature, plus the additional cost of delay since it look longer to put it into production. We'll incur a cost of carry on every feature built between now and the time the piracy insurance software starts being useful. Should we never need the piracy-pricing software, we'll incur a cost of carry on every feature built until we remove the piracy-pricing feature (assuming we do), together with the cost of removing it.

 

 지연비용은 필요로하게될 기능이 만드는 비용 중 하나이지만, 다른 하나는 캐리 비용이다. 예상되는 기능을 추가하기 위해 소프트웨어에서 코드의 복잡도가 올라가게 되고, 이 복잡도는 소프트웨어의 디버깅과 수정을 어렵게 하여 다른 기능의 비용을 상승시킨다. 소프트웨어에서 그 해적관련 pricing 기능으로부터 추가된 복잡도는 폭풍 관련 보험 sales 컴포넌트를 만드는 기간을 2주 추가시킬 수도 있다. 그 2주는 두가지 방식이 된다 : 기능을 만들기 위한 추가적인 비용, 플러스 추가적인 지연비용, 제품에 넣는 것이 더 오래 걸리기 때문에. 우리는 지금부터 해적관련 보험 소프트웨어가 쓸모있어지는 순간까지 캐리 비용을 발생시키게 될 것이다. 만약 해적 관련 pricing 소프트웨어가 쓰이지 않는다면, 해당 기능을 지우기 전까지 모든 기능에 캐리 비용을 만들어내게 될 것이고, 지우는 것에도 비용이 든다.

 

So far I've divided presumptive features in two categories: successful and unsuccessful. Naturally there's really a spectrum there, and with one point on that spectrum that's worth highlighting: the right feature built wrong. Development teams are always learning, both about their users and about their code base. They learn about the tools they're using and these tools go through regular upgrades. They also learn about how their code works together. All this means that you often realize that a feature coded six months ago wasn't done the way you now realize it should be done. In that case you have accumulated TechnicalDebt and have to consider the cost of repair for that feature or the on-going costs of working around its difficulties.

 

 지금까지 예상되는 기능을 두가지 카테고리로 나누었다 : 성공적이거나 아니거나. 기본적으로 범위가 있고, 해당 범위에 가치 있는 부분이 있다 : 옳은 기능이 잘못 만들어진 것. 개발팀은 항상 배워야하는데, 그들의 사용자와 그들의 코드 베이스 둘다 이다. 그들은 사용하는 툴에 대해 배우고, 툴들은 일반적인 업그레이드가 된다. 그들은 또한 그들의 코드가 어떻게 다같이 동작하는지 배워야 한다. 이 모든 것들은 네가 종종 지금 이렇게 했어야 했다고 생각드는 방식으로 끝나지 않은 6개월 전에 쓰여진 기능 코드를 깨닫게 하는 것이다. 그러한 경우에 넌 기술적인 빚을 쌓게 되고, 수리 비용을 고려하게 되거나 난이도에 관하여 일을 하는 진행 비용을 고려하게 된다.

 

 So we end up with three classes of presumptive features, and four kinds of costs that occur when you neglect yagni for them.

 

 3개의 추정되는 기능들의 클래스와, Yagni 를 무시했을 때 발생할 수 있는 4가지의 비용으로 정리한다.

 

어떤 식으로든 캐리비용과 지연비용은 무조건 포함된다.

 My insurance example talks about relatively user-visible functionality, but the same argument applies for abstractions to support future flexibility. When building the storm risk calculator, you may consider putting in abstractions and parameterizations now to support piracy and other risks later. Yagni says not to do this, because you may not need the other pricing functions, or if you do your current ideas of what abstractions you'll need will not match what you learn when you do actually need them. This doesn't mean to forego all abstractions, but it does mean any abstraction that makes it harder to understand the code for current requirements is presumed guilty.

 

 위에서 예를든 보험에 관한 이야기는 상대적으로 유저가 확인가능한 기능성이지만, 미래의 유연성을 지원하기 위한 추상화에 같은 논쟁이 적용된다. 폭풍우 리스트 계산기를 구현할 때, 아마 추상화와 파라미터화를 고려해서 나중의 해적질이나 다른 리스트를 서포트하려 할 것이다. 야그니는 이것을 하지말라는 것인데, 왜냐면 아마 다른 pricing 기능들이 필요없을 것이거나, 그러한 것들이 실제로 필요하게 되었을 때 딱 맞아떨어지지 않을 것이라는 내용이다. 이것은 모든 추상화에 앞서가라는 것은 아니지만, 어떤 추상화든 현재 요구사항의 코드 이해를 어렵게 하는 것은 죄악이라고 생각해야 한다는 것이다.

 

 Yagni is at its most visible with larger features, but you see it more frequently with small things. Recently I wrote some code that allows me to highlight part of a line of code. For this, I allow the highlighted code to be specified using a regular expression. One problem I see with this is that since the whole regular expression is highlighted, I'm unable to deal with the case where I need the regex to match a larger section than what I'd like to highlight. I expect I can solve that by using a group within the regex and letting my code only highlight the group if a group is present. But I haven't needed to use a regex that matches more than what I'm highlighting yet, so I haven't extended my highlighting code to handle this case - and won't until I actually need it. For similar reasons I don't add fields or methods until I'm actually ready to use them.

 

 야그니는 큰 기능들에서 가장 눈에 띄지만, 작은 것들에서 더 자주 확인할 수 있다. 최근에 나는 코드의 라인 일부를 하이라이트 할수 있게하는 몇 코드를 코딩했다. 이것을 위해서, 난 정규표현식을 써서 하이라이트 된 코드를 특정지었다. 이 문제는 모든 정규표현식이 하이라이트 되기 때문에, 내가 원하는 하이라이트 부분보다 넓은 섹션을 필요로 하는 케이스를 다룰 수 없었다. regex 안에서 그룹핑을 해서, 해당 그룹이 있으면 그 그룹을 하이라이트 하는 식으로 풀 수 있을 것이라 생각했다. 하지만 내가 하이라이트 하는 것 이상을 매칭하는 정규표현식이 필요치 않았는데, 그래서 이러한 케이스를 다루기 위한 하이라이트 코드를 확장하지 않았으며 - 실제로 필요할 때까지 하지 않을 것이다. 비슷한 이유로, 필드나 메소드를 실제로 필요 할 때 까지 추가하지 않는다.

 

 Small yagni decisions like this fly under the radar of project planning. As a developer it's easy to spend an hour adding an abstraction that we're sure will soon be needed. Yet all the arguments above still apply, and a lot of small yagni decisions add up to significant reductions in complexity to a code base, while speeding up delivery of features that are needed more urgently.

 

 이와같은 작은 야그니 결정사항들은 프로젝트 계획의 레이다 아래에 있다. 개발자로서 곧 필요로 하게될 추상화를 추가하며 한시간 쓰는 것은 쉽다. 하지만 위의 모든 논쟁들이 적용되고, 많은 작은 야그니 결정들은 코드 베이스의 많은 복잡도를 줄이며, 더 긴급하게 필요로하는 기능들의 속도를 올려준다.

 

 

 이하 생략

 

 Now we understand why yagni is important we can dig into a common confusion about yagni. Yagni only applies to capabilities built into the software to support a presumptive feature, it does not apply to effort to make the software easier to modify. Yagni is only a viable strategy if the code is easy to change, so expending effort on refactoring isn't a violation of yagni because refactoring makes the code more malleable. Similar reasoning applies for practices like SelfTestingCode and ContinuousDelivery. These are enabling practices for evolutionary design, without them yagni turns from a beneficial practice into a curse. But if you do have a malleable code base, then yagni reinforces that flexibility. Yagni has the curious property that it is both enabled by and enables evolutionary design.

 

 Yagni is not a justification for neglecting the health of your code base. Yagni requires (and enables) malleable code.

I also argue that yagni only applies when you introduce extra complexity now that you won't take advantage of until later. If you do something for a future need that doesn't actually increase the complexity of the software, then there's no reason to invoke yagni.

 

 Having said all this, there are times when applying yagni does cause a problem, and you are faced with an expensive change when an earlier change would have been much cheaper. The tricky thing here is that these cases are hard to spot in advance, and much easier to remember than the cases where yagni saved effort [4]. My sense is that yagni-failures are relatively rare and their costs are easily outweighed by when yagni succeeds.

Further Reading

My essay Is Design Dead talks in more detail about the role of design and architecture in agile projects, and thus role yagni plays as an enabling practice.

This principle was first discussed and fleshed out on Ward's Wiki.

Notes

1: The origin of the phrase is an early conversation between Kent Beck and Chet Hendrickson on the C3 project. Chet came up to Kent with a series of capabilities that the system would soon need, to each one Kent replied "you aren't going to need it". Chet's a fast learner, and quickly became renowned for his ability to spot opportunities to apply yagni. Although "yagni" began life as an acronym, I feel it's now entered our lexicon as a regular word, and thus forego the capital letters.

2: In this post I use "presumptive feature" to refer to any code that supports a feature that isn't yet being made available for use.

3: The ⅔ number is suggested by Kohavi et al, who analyzed the value of features built and deployed on products at microsoft and found that, even with careful up-front analysis, only ⅓ of them improved the metrics they were designed to improve.

4: This is a consequence of availability bias

Acknowledgements

 Rachel Laycock talked through this post with me and played a critical role in its final organization. Chet Hendrickson and Steven Lowe reminded me to discuss small-scale yagni decisions. Rebecca Parsons, Alvaro Cavalcanti, Mark Taylor, Aman King, Rouan Wilsenach, Peter Gillard-Moss, Kief Morris, Ian Cartwright, James Lewis, Kornelis Sietsma, and Brian Mason participated in an insightful discussion about drafts of this article on our internal mailing list.