Seattle voice: talking with Sergei Teplyakov
Under a cat there is a lot of text about conferences, TDD, pair programming, architecture Code Contracts, Habr.
About conferences from the perspective of the Listener
Sergey, good afternoon. Why do people attend conferences? Why visit them? To be honest, I really did not attend conferences if I was not a speaker there. But every time I was there, the main thing that I brought out from there was inspiration. Yes, it may sound overpriced, but conferences and communication with smart colleagues have always been the best motivation for me: to learn something new, share something old or just continue my development as a specialist and as a person.
You read and reviewed many books. What can you advise in terms of conferences?
It is rather difficult to give a specific question to a rather general question. It is important to understand what exactly you need there: recharge your batteries? Get to know and / or chat with interesting people? Replenish any specific knowledge? If the latter, then instead of a conference you need to take a couple of sensible books, but if one of the first points, then again you need to find out the quality and interestingness of the conference and decide for yourself whether to go or not.
How to evaluate the conference on the program?
The conference needs to be judged by the speakers and you need to go not on the topic, but on the person.
If Andrei Akinshin speaks, then I’ll go to listen to him no matter what he’s talking about, about Stopwatch or about a topic that is not related to performance at all. Since I am sure that the quality will be high and I will learn a lot from it.
Ok, how do you rate the speakers?
Pretty simple, actually. If the speaker is known for good performances - go. If the speaker is not familiar, but a fluent googlezh brings you to his blog, github or habr - you evaluate the material. If the speaker is not familiar and does not google - it is better to go for something else.
About conferences from the point of view of the Speaker
With a visit it’s clear. Can you tell me why people speak at conferences?I think this is a question of self-realization or confirmation of a certain level of development: “Yes, I have grown to a certain level when I am not afraid to go out to many people and share my experience: both positive and negative. I’m not ashamed to admit that I don’t know, because I know enough useful things that are interesting to share. ” For me, speaking in general, and at conferences in particular, was such an indicator of development. It all started with a conversation over a cup of coffee, or in the process of pouring into a new team, when you begin to communicate more confidently with colleagues, sharing experience, defending your opinion. After that, there are local performances at all kinds of meetings and user groups, after which it is no longer scary to speak to several hundred.
A separate plus: the validation of knowledge in the preparation process. You know the phrase: if you want to learn something, teach another. Preparation for a performance can take 10-20 times longer than the performance itself, and all this time you deliberately dig one topic. Of course, there are experts who tell / write something interesting without a plan, but there are few of them. I can recall only Chris Broome, one of the architects of CLR. He wrote most of the posts directly from his head, and once made an introduction “I used to raise topics that did not require additional research from me. "I gave this article for a preliminary review, because for some questions I had no answers." So the conference is still a motivator to study something in more detail, to structure knowledge.
But how to understand that “now I know enough useful things, and I really have something to tell about”?
As I said, the easiest way to run the material "on the cats." It can be coffee gatherings, corporate speeches, blog notes or in any other way.
It is very important to validate your knowledge on someone, so that it doesn’t turn out that your knowledge is not very consistent with reality or is not so deep and / or interesting. There is an option to validate your experience using githubs and other open source. If you fixed a couple of bugs in Roslin and proposed a design option that was approved by the project’s old-timers or other participants, then this should be sufficient evidence of your experience and maturity.
That is, you are not preparing in detail? Only canvas?
Canvas, obligatory interlinear (although I don’t look at it during the performance), and that’s it. The difference between speaking and writing a post is not that big: introduction, story, conclusion; you need to give a thought, you wonder what the reader knows, what places need to be explained, which ones just to mention. True, you can refer to something in the post, but at the conference saying “guys, first read these three articles, and then come back” will fail.
Do you perform in the States?
Not yet. Priorities are different. Plus, the level of spoken English does not allow you to speak fluently. As I said, I usually don’t say everything in advance, but simply prepare a plan and talk about it, and in such things, language is very important. It is difficult to speak when you cannot say anything beyond the prepared, when you are not even able to joke. And confidence is needed. We have a tradition in MS: sometimes guys gather as a team or project in a room with lunch, and there someone tells colleagues about interesting technical things. While I speak only there.
What is the audience for your MS gatherings? Compared to the conference?
Among the listeners there are sometimes people who come to measure patterns. This usually does not happen among colleagues, but it happens at conferences. You must be able to work with such comrades.
I am ashamed to admit, but once I acted as such a comrade. How to work with such?
Here you need to understand what the listener wants; perhaps, asking a question, a person is really trying to understand something, for example, he is a special subcontractor, or just a colleague with rich experience in another language. If you think that you can answer the question, but it’s long \ difficult - offer to discuss after the speech. If he brings you to the answer “I don’t know” - say “I don’t know, google it, I will contact you after”. There are times when you refer to Richter, and disputes begin with you like “Richter didn’t say so ...”, “this wasn’t ...”, such disputes are usually unconstructive and it’s enough to refer to “after the speech”, politely.
TDD
Classic scheme: test code refactoring. As far as I understand from your articles, you follow it insofar as. How to determine when to follow and when not to? How can I keep from rolling to “oh well, the whole project is already without tests, and I won’t write anything about this feature”? Honestly, I hardly use TDD in its classic presentation. I write a lot of tests, but I do not write them before the code. My loop looks something like this:
- We look at the problem and try to see the high-level components. Perhaps we take a pencil and draw small squares with relationships. This allows you to see the whole picture and what subcomponents it includes. After that, you can sketch a high-level test case, but usually I do without it.
- After that, we start decomposing the system from the bottom up (before that, we tried to parse / paint the problem according to the principle from top to bottom). We take one of the leaf classes and begin to think what it is.
- After that, I move on to implementation, lay out function frameworks, possibly setting up contracts.
- Then I proceed to implement a specific function and only after writing it add a test.
It turns out that test-first is not at all, but it’s more convenient for me to “see” the design in the code and already validate its correctness using tests. It’s not convenient for me to “grow” the design with the help of tests; for me, switching from “test” to “code” is very distracting.
Now let's move on to the second question: "how to resist rolling". Everything is simpler here.
A few years ago, I was promoting ("selling") unit testing to a customer. And neither then nor now did I focus on the regression component, on the quality of design, etc. I tried to show how the tests help here and now: “ok, we need to add a new feature. But instead of checking its sanity by deploying a server and then clicking on 73 points in the UI, why not add a simple unit test that will test the most complex functionality? Yes, you’ll have to think a bit about how to separate this feature into a separate component, but it’s faster than all this dreg with manual testing. ”
In other words, if I need to make a minimally sane addition of non-trivial complexity, I will think about how to put the new logic into a separate class / method so that it can be easily tested. When everything is very running, it is not always easy to do and it does not always work, but, as it is not strange, for me it works in most cases.
It happens that everything is heavily launched and the new functionality is a variation of the old when changes are made to the group of existing classes. In this case, we think about how to cover all this stuff with integration tests, and then we decide whether the wed-up of the existing code will be justified, will there be significant changes in these pieces or not? If we are talking about bad code that we are going to actively develop, then I will try to justify more sane changes before making significant functional changes. If the code is dead and will not really develop, then you will have to test with pens.
When do you need to write tests? Or are you testing everything, down to PS scripts?
There are a number of things to cover with tests that make no sense. PS scripts are one of them. Since PowerShell by its nature works closely with the environment, it is very difficult to test scripts in isolation. In addition, PowerShell is a little crazy thing, and without real data, you simply won’t know what to expect from it and whether your script works or not.
There are quite a few things that I do not test directly. If there is a lot of legacy code, then several integration tests are needed, and unit tests with high coverage should already be written for new components. There are things that should be covered by tests, but these will not be unit tests, but integration ones, which are more likely to check the basic validity. This includes tests of database and communication stuff, such as WCF.
As with many other things, you need to understand that tests are a tool, not an end in themselves. In this case, you need to change the attitude to the tool in the process. If there are a lot of tests, and they catch constant regressions - that’s good. If there are a lot of tests and they constantly break down, then this is bad. But here it is important not to rush from one extreme to another, not to nail all the tests in one fell swoop, considering them useless. Here you need to think about how we test, what we test, is our design good? Usually fragile tests are a symptom of a more general problem - problems with design, high connectivity, and with tests that check implementation details, rather than abstraction behavior.
Pair programming
How does writing tests combine with pair programming? You had a couple of articles on pair programming, but I did not notice a direct connection with the tests. Theoretically, working in a pair implies separation of roles, when one is testing, and the second is logic. Unfortunately, I do not have too much experience of classic pair programming, in which we would follow this principle.
As a result, I always worked in pairs so that it was comfortable for both. This means that the tests were written as we considered necessary. Often these were integration tests that covered rather large chunks, after which we worked together on the implementation. We then added complete tests one by one.
But here you need to understand that if TDD is to some extent a personal matter, then TDD in a pair is a matter of this very pair. The couple must decide how to be effective. In my work, we did not hesitate to talk about what is now worth doing and what is not. There should not be any dogmas.
How to negotiate with aggressive partners, as with shy, closed?
I don’t have much experience, yet pair programming is a rather rare approach in the industry, both in the States and in Ukraine. We have a lot of smart people on projects, but I'm not ready to miss out with a partner “from the ceiling”. In my opinion, pair programming is ineffective if one person is accurate in the code, the other is not, or one writes quickly, the other is thoughtful, or the other one is shy. If I were in such a pair - I would try to avoid this. Very few people will approach me purely psychologically. I’m talking about pair programming. If you need to mentor - I think 90 percent will suit me, I will find an approach to them.
How did you start coding in pairs?
I participated in a hackathon, and stuck with one of my colleagues to create the next version of Code Search, this is a search engine forreferencesource.microsoft.com - the previous version kept a lot of extra data in memory. I had experience suitable in Elastic Search, I stumbled. You know the hackathons: time is short, you have to do a lot, there was no time and opportunity to divide the work, and we sat at the same computer typing one code. It turned out well, and since then some tasks have been coded in a couple, since a few months later we ended up on the same project. But once again, this is a rarity. In Ukraine, I did not hear about this at all, in MS I heard about one team practicing regular pair programming, but this is a drop in the bucket.
It has happened with colleagues that we argue for some reason a little longer than necessary, too unstable. Such discussions can be considered PP?
This is always the case, especially if someone is new to the team. When you get used to it, you’ll be hungry - you can try to pair up. When you already find out who is worth what, when you realize that “well, a stubborn person, but sensible thoughts, well, yes, I’m not a gift either, but also bring benefits”, then you can try. Before that, it will be quite difficult to communicate, and there will be little benefit. If you worked with a pair programming pro who adapts very quickly, you could try, but this phenomenon is even more rare than the usual “greenhouse”.
How do you prepare for pair programming?
No way. At all. Unless, it is worth looking into the context before starting work, but nothing is needed at all.
How does the language barrier interfere?
Generally does not interfere. General project, common programming language, general subject area. In the most extreme case, you can take a piece of paper and draw something on it.
Instrumental barrier?
Yes, this is a classic. My buddy R # is a hater, I’m rather the opposite. Sometimes we laugh with that. That is, you code like this, put a curly bracket, then you have all the alignment flies out of habit, and you are like, “stop, potty, don’t cook, Ctrl + Z, everything is canceled ...”. It’s like little things, other hyphenation, brackets, hotkeys, but it’s fun to watch the process. On the other hand, a good way to learn the skills of using tools. Recently, I’ve heard phrases like “I don’t want to admit, but I like this R # feature”. That is, at first the person did not like R # because of the brakes on large projects, but now he begins to appreciate. Plus, we had differences in tools for diff-merge, it was interesting to look at the analogue. In general, I like observation most of all in the process, from the approach to design to the tools used. That is, you do not read the manual, and you watch how a living person copes with the task "on the fly." Very comfortably.
Code contracts
You are one of the developers of Code Contracts. How did you start working with the project? I am a longtime fan of contract programming. My love began after I became acquainted with Bertrand Meyer's book “Object-oriented construction of software systems”. Thinking about the design in terms of responsibility can significantly simplify the development, allows you to make it cleaner.
The CodeContracts library has long existed on the .NET platform for these purposes, and I have been using it for a long time. About a year before moving to MS, I wrote a pluginfor R # to simplify contract programming, and use this. The plugin catches typical Code Contracts errors, allows you to add Contract.Requires (arg! = Null) contracts, and does a number of other useful things. In my first year at MS, I made a small tool for Applications Insights. She proved useful to Mike Barnet., one of the authors of Code Contracts. Then I moved to another team, and it turned out that this team is using Code Contracts with might and main, and this is one of the largest users of Code Contracts in MS. After the release of VS 2015, we could abandon Code Contracts, or fix the library so that it could support C # 6.0. As a result, I was actively engaged in finalizing Code Contracts for one and a half to two months. It was during this period of time that I was most active on github. In addition to adding new features, a number of features were fixed that Code Contracts never normally supported, in particular, postconditions in asynchronous methods. After that, I became one of the library's maintainers. Now I do the project in my free time, but I do it much less than I would like.
Why?
Other priorities.
New project?
New but related to Code Contracts.
I do not understand.
Code Contracts has some serious issues that make development difficult. The project is large, complex, hundreds of thousands of lines in places of confusing code. In addition, the library is designed to analyze and modify IL code, without being tied to a specific .NET language or compiler.
The library contains three main components.
- Infrastructure (Common Compler Infrastrucutre, CCI) - decompile IL into an object model (Abstract Syntax Tree, AST). AST at the output is mutable, low-level. No closures, no async, no iterator blocks.
- Static verifier CC Check, checking the validity of contracts during the build. That is, it parses AST, and if it sees a method with NotNull “annotation”, it tries to prove that Null will never come to this method.
- Runtime IL Rewriter (CC Rewrite). Engaged in the transformation of dll files during build. Converts calls to the Contract class to various constructs: to throw an exception, to Debug.Assert, or simply removes the whole statement. This is a separate utility that is not associated with the verifier.
This approach has an important feature: different compilers and even different versions of the same compiler generate different IL-code for the same syntax constructs. And CCI provides only a thin wrapper over IL: there are no lambda expressions, iterators, or asynchronous methods. You see that you are creating an object of a certain class, but you do not know if this is a real class created by a programmer, or generated by compilers as a result of using a lambda expression. More precisely, you can find out about this, since compilers use special patterns for generating code. Therefore, ccrewrite analyzes the code, looks at the type names and determines from it whether it is a closure class, a class for an iterator or asynchronous method, or something else.
In this case, the patterns change in different versions of the compiler. For the same lambdas, C # 5.0 generated a static field with a delegate, and a new type of closure classes appeared in C # 6.0. C # developers have found that instance methods are cheaper than static methods, so now a singleton is generated for "non-catching" lambda expressions instead of a static variable.
A completely crazy example can be given for asynchronous methods. The C # 6.0 compiler generates different code depending on the number of await statements in the method: if there are no await s, one code is obtained, if await is only one, then the other, and so on.
And we must consider all these details. Each time the compiler begins to process the language construct differently, both the verifier and the Rewriter need to be changed. Each change of language is a big and serious work: you need not only to add support for new language constructs, but also to check that all old constructions also continue to work, and even maintain backward compatibility.
Separate problem: it is very difficult to fix bugs. You can sit for 8 hours for two lines of fix. So I struggled with one of the bugs when in a generated generalized method inside a generalized class, in a certain case, the argument type was! T, not !! T (this means that a generalized type parameter was used, not a generalized method parameter).
In general, the AST generated by CCI is too low, which translates into too complex support. With each new version of the language \ compiler, more and more hard and thankless work has to be done.
Another problem: the negative impact of CC Rewrite on build time. CC Rewriter is non-deterministic. That is, you take one DLL, call CC Rewriter twice - and you are guaranteed to get two different binaries. For modern build systems this is a very big problem; in fact, the ability to cache and incremental builds is lost. That is, if you use CC Rewrite and make a change to one file - you are guaranteed to have to rebuild the whole solution. If you do not use CC Rewrite, you can rebuild only one project and all its direct clients. On hundreds of projects, using CC Rewrite increases build time significantly.
Plus, CC Rewriter downloads pdb when rewriting, and it doesn’t always do it correctly, which negatively affects the debug. From a recent one, my colleague simply could not engage in debugging without disabling CC Rewriter, a call stack pops up, local variables are displayed incorrectly, the entire debug experience was lost.
Because of these problems, my colleague and I are currently working on the Source Level Rewriter (SL Rewriter). That is, we do not operate on IL, we change the high-level code. Of the shortcomings, this is only a utility only for C #.
That is, you take the Roslyn tree, modify it, and give it to the compiler? The usual fixer?
Exactly. Roslyn provides exactly the mechanism that we need. We take our Workspace, look for all calls to contracts, and insert the fragments we need.
If you use Roslyn, are you limited to C # 6.0?
Nearly. In fact, we are more concerned with integration with the compiler, but this is a topic for another discussion.
Where can I find the Source Level Rewriter?
Now this is an internal product that 2 people are sawing. He is not ready yet, but as soon as he will work correctly in our team, we will share it.
Will there be predictions about performance? Now, Code Contracts is often scolded for being "slowed down."
Look, CC Check is very slow. For correct operation, you have to place a lot of annotations, and the build of one hundred thousand lines of code will take minutes, or even tens of minutes. Then we still need to analyze the conclusion. In short, a slow and inefficient tool from the point of view of the developer. More precisely, the idea is good, but the task is very difficult and slowly being solved. I know just a few teams that use it actively. My team abandoned it two years ago and no one wants to use it again.
Now CC Rewrite. As I said, it significantly affects the build process. There are finite tricks, for example, to disable it locally and use it only on the build machine, but there is still a slowdown.
Much more important is the effect on runtime. With contracts, it’s very easy to breed extra checks. So, if you have heavy invariants on a class, checks will be added to the beginning and end of each open method. Or collection contracts - you have to iterate the whole collection. This can be especially noticeable if you have a chain of five methods running through the collection. Or a recursion check to restrict calling contracts in depth. It uses a call to thread local, which adds brakes. By simply disabling this check, we increased end-to-end performance by 15%. There are not many such details, but you should know about them.
On the other hand, we are now sawing a build engine, it is a system software and a considerable code base. We have to tune the contracts to minimize the impact on runtime performance, but we don’t want to refuse the contracts: the messages from the contracts in the received crash dumps provide a lot of useful information, it is much more difficult to achieve this using other standard tools.
In addition, it must be said that for most applications the overhead for runtime will be minimal.
That is, for small projects, the tool can be used without settings?
For small ones, yes. For mediums, you can put only a check of preconditions and open methods, then the impact will be minimal. The key functionality, carefully covered by tests, can be configured to remove all contracts from runtime. The tool is customizable - it is quite possible (and worth) to use it. It will have to be tuned only on large projects.
Questions about Habra.

Wow ... it has been a long time. I came to Habr in 2010, when rsdn was already dying, dou.ua was already, but was not so popular. Habr at that time was actively developing, with many deep technical posts and, most importantly, with a high level of signal-to-noise. It was interesting to read, it was interesting to write. Publication there - it was the easiest way to discuss some kind of technical problem. Yes, not all comments were equally useful, but at that moment it was interesting to me.
Why did you leave Habr?
First of all, I have changed, my interests have changed. I continue to write on my blog, often choosing more philosophical topics, and there has already developed a certain audience with which it is interesting, who knows me and my interests. To write in parallel on a blog and on Habré - not absolutely "correctly". After my last “parallel” posts collected more comments on the blog, I practically stopped writing on Habr.
I still read Habr, but more often thanks to social networks and other methods. The noise level by reading via RSS is too high for me: apparently, I am not interested in everything that is interesting to the Habr audience, which has become even more diverse. At the same time, I often find myself on excellent articles, thoughtful, deep and interesting.
In addition, Habr is the largest IT portal with which you can draw a lot of useful information. Studying a specific topic, I still often find myself there if I'm looking in the Russian-language segment.
From the author: our conversation with Sergey came out somewhat chaotic and immediately touched on several unrelated topics. If I missed something - Sergey is available in PM and comments. The answer may go more than usual, consider the time zone: Seattle’s time differs from Moscow by 11 hours.