forked from markkampe/Operating-Systems-Reading
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstability.html
300 lines (298 loc) · 13.5 KB
/
stability.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
<HTML>
<HEAD>
<TITLE>Interface Stability</TITLE>
</HEAD>
<BODY>
<CENTER>
<H1>Interface Stability</H1>
Mark Kampe<br>
</CENTER>
<P>
<H2> Interface Specifications</h2>
<P>
American History books like to credit Eli Whitney with the invention
of manufacturing with interchangeable parts. Actually,
<UL>
<LI> Whitney's parts weren't all that interchangeable,
<LI> he got the idea from a French gunsmith (Honore Blanc),
<LI> the Swedish clock maker Christopher Polhem did a much
better job with clock gears 50 years earlier.
</UL>
But, be that as it may, interchangeable parts revolutionized,
and in fact enabled modern manufacturing.
The underlying concept is that there be specifications for every part.
If each part is manufactured and measured to be within its specifications,
then <strong>any</strong> collection of these parts can be assembled
into a working whole. This greatly reduces the cost and difficulty
of building a working system out of component parts.
<P>
The same principle applies to software. If you want to open a file
on a Posix system, you use the <strong>open</strong> system call.
There may be a hundred different implementations of open but
this doesn't matter because they all conform to the same interface
specification. The rewards for this standardization are:
<OL type=a>
<LI> Your work as a programmer is simplified, because you
don't have to write your own file I/O routines.
<LI> Your program is likely to be easily portable to any
Posix compliant system, because they all provide
the same file I/O services.
<LI> Training time for new programmers is reduced, because
everybody already knows how to use the Posix file
I/O functions.
</OL>
<H2>The Criticality of Interfaces in Architecture</h2>
<P>
A system architecture defines the major components of the system,
the functionality of each, and the interfaces between them.
Clearly the component interface specifications are a critical element
of any architecture. To the extent that these interfaces have been
well considered and well defined, it should be possible to (at least semi)
independently design and implement those components. In principle, any
implementations that satisfy those functional and interface specifications
should combine to yield a working system.
<P>
The converse is also true. To the extent that interfaces between
components were poorly considered or specified, it becomes unlikely
that independently developed components will work when combined
together, and more likely that subsequent changes to one component
will break others.
<P>
<H2>The Importance of Interface Stability</h2>
<P>
We write contracts is so that everybody knows what
will be expected of them, and what they can expect from the other
parties to the agreement. Everybody will depend on you to uphold
your obligations under the contract. A software interface specification
is a form of contract:
<UL>
<LI> the contract describes an interface and the associated
functionality.
<LI> implementation providers agree that their systems will
conform to the specification.
<LI> developers agree to limit their use of the functionality
to what is described in the interface specification.
</UL>
And in return for this they get all the benefits described above.
<P>
Suppose that some architectural genius realized that Posix file
semantics are too weak to describe the behavior of files in a
distributed system, and that this could be solved by adding a
new (required) parameter (for a call-back routine) to the Posix
open call. What would happen?
<UL>
<LI> software written to the new semantics would have access
to richer behavior, and would function better in cases
of node and network failures.
<LI> software written to the new semantics would not work on
other Posix compliant systems.
<LI> software written to the old semantics would not work on
the new systems.
<LI> customers (knowing nothing about this techno-feud)
would be faced with inexplicable failures, and
would start complaining to their software and system
providers (none of whom were responsible for the change).
<LI> programmers would be confused about which version of open
they were using, and would probably get around the problem
by writing their own file I/O packages ... a bunch of extra
work, but at least it would protect them from future such
acts of mischief.
</UL>
And as a result of this, we would lose all of the benefits described
above. When you promise somebody that you will do something (e.g.
conform to a specified interface),
and they depend on you (e.g. by writing code to that interface),
and you don't follow through (e.g. make an incompatible change) ...
problems are likely to ensue (e.g. failures, bug reports, product
gets a bad reputation, going out of business, etc.).
<P>
<H2>Interfaces vs. Implementations</h2>
<P>
Suppose that someone writes a handy library to efficiently provide
reliable communication over an unreliable network, and I decide
I want to use it. I download their development kit and start using
it, and find it to be great. A few weeks
into the project I realize that I need a feature that is not included
in their documentation, but after reading their code I discover that
the needed feature is actually available. I use it and my product
is a great success.
<P>
Six months later, they release a new version of their reliable
communications library, and my product immediately breaks. After
a little debugging I discover that they have changed the
undocumented code that I was depending on. It turns out that I
had not designed my product to work with their <em>interfaces</em>.
My product only worked with a particular <em>implementation</em>
... and implementations change.
<P>
People often put much more work into designing and maintaining
their code than to clarifying and maintaining their documentation.
This makes it tempting to reverse engineer the interface specifications
from a provided implementation. But an interface should exist
(and be defined) independently from any particular implementation.
Confusing an implementation with an interface usually ends badly!
<P>
<H2>Program vs. User Interfaces</h2>
<P>
Human beings are amazingly robust. We could change the text in
dialog boxes, rearrange input forms, add new required input
items, and rework all of the menus in a program, and many users
would figure out the changes in a few seconds.
This inclines many developers to be cavalier in making changes to
user interfaces.
<P>
Code is nowhere nearly so robust. If I expect my second parameter
to be a file name, and you pass me the address of a call-back routine
instead, the best we can hope for is a good error message and a
quick coredump with no data corruption.
<P>
If all users were developers, and only ran their own code, this might
be just an irritation. We would get the core dump, track it down,
figure out that some idiot had made this change, recode accordingly,
and an hour later we'd be good as new. Unfortunately most people
run code that they do not understand, and have no way to debug or
fix. If a program breaks, all I can do is call support and complain.
I am helpless. Users don't care which programmer goofed up.
"I bought your product. It doesn't work. I'm taking my business
elsewhere!".
<P>
If you are going to be delivering binary software to non-developers
(that describes 99.999% of all software) you have to trust that
what ever platform they run it on will correctly implement all of
the interfaces on which your program depends.
<P>
The same argument applies, though not quite as strongly, to independent
components in a single system. If components exchange services, and
I make an incompatible change to the interfaces of one component, this
has the potential to break other components in the same system. I can
fix the other components in our system to work with the new changes
but:
<UL>
<LI> this complicates the making of the change.
<LI> we have to ensure that mismatched component sets are impossible.
</UL>
<P>
<H2>Is every interface carved in stone?</H2>
<P>
Absolutely not.
<P>
You are free to add features that do not change the interface
specification (a faster or more robust implementation). In
many cases you can add upwards-compatible extensions (all old
programs will still work the same way, but new interfaces enable
new software to access new functionality). There are a few
ways to add incompatible changes to old interfaces without
breaking <em>backwards compatibility</em>:
<UL>
<DL>
<DT><strong>Interface Polymorphism</strong></DT>
<DD><P>
In old-school languages (like FORTRAN and C) a routine had a
single interface specification. But more contemporary programming
languages support polymorphism (different method signatures with
different parameter and return types).
If different versions of a method are readily distinguishable
(e.g. by the number and types of their parameters), it may be
possible to provide new interfaces to meet new requirements,
while continuing to support the older interfaces for
backwards compatibility with older applications.
</P></DD>
<DT><strong>Versioned Interfaces</strong></DT>
<DD><P>
Backwards compatibility requirements do not prevent us from
changing interfaces; they merely require us to continue
providing old interfaces to old clients.
In many cases, it may be possible for an application to call
out which version of an interface it requires:
<ul>
<li> Java programs are labeled with the run-time-environment
versions they require.</li>
<li> Network protocol (e.g. RPC) sessions often begin with
a version negotiation, where each side states the
interface versions that it can support, so that a
mutually acceptable version can be chosen.</li>
</ul>
This approach does permit incompatible interface changes, but
still requires servers to support old as well as new interface
versions.
</P></DD>
</DL>
</ul>
<P>
But even if we have no ability to deliver multiple interface
versions, this does not necessarily mean I can never make
incompatible changes. It is not the case that all interfaces must be supported
for all of time. It is merely necessary that the interface
stability be understood, and adequate to achieve the product
(or project) goals:
<UL>
<li> It is common to provide alpha/evaluation
access to proposed new services/interfaces. This is
done with the understanding that the final interfaces
are likely to be different. Such releases are provided
purely for evaluation purposes. Understanding the
likelihood of change, developers should only use them
for prototyping, and should not base long-lived products on them.</li>
<li> Many suppliers have interface stability guidelines for their
releases. A typical example might be:
<ul>
<li> a micro-release (e.g. 3.4.1) includes only bug fixes.</li>
<li> a minor-release (e.g. 3.5) includes new functions,
but us upwards compatible with the underlying major
release version.</li>
<li> a major release (e.g. 4.0) is allowed to make
incompatible changes to previously committed
interfaces.</li>
</ul>
Applications software developers can use this to
determine which versions their software will run on,
and customers can use this to decide whether or not
they can safely upgrade to a new release.
<li> Many suppliers have make specific support commitments
(e.g. we will support release 3.5 for five years from
its initial availability date). This gives software
developers and customers confidence that they will
have a stable software platform for at least that
long.
At the end of that period, customers may have to
choose between continuing to run on a no-longer-supported
platform or moving to new versions of their mission-critical
applications.
</li>
</ul>
<P>
How stable an interface needs to be depends on how you intend
to use it. What is important is that:
<OL type=1>
<LI> we be honest about our intentions and set realistic expectations.
<LI> we have a plan for how we will manage change.
</OL>
<H2>Interface Stability and Design</h2>
<P>
So, if we want to expose an interface to unbundled software
with high quality requirements, we are not allowed to change
it in non-upwards-compatible ways.
That sounds like a bother, but if that is the rules, so be it.
What has this got to do with architecture and design?
<P>
When we design a system, and the interfaces between the independent
components, we need to consider all of the different types of change
that are likely to happen over the life of this system. When we
specify our external component interfaces we need to have high
confidence that will be able to accommodate all of the envisioned
evolution while preserving those interfaces.
<UL>
<LI> We might rearrange the distribution of functionality
between components to create a simpler (and hence
more preservable) interface between them.
<LI> We might design features that we may never implement,
just to make sure that the specified interfaces will
accommodate them if we ever do decide to build them.
<LI> We might introduce (seemingly) unnatural degrees of
abstraction in our services to ensure that they
leave us enough slack for future changes.
</UL>
We must consider which of our interfaces will need to be how stable,
and design our system with those stabilities in mind.
</BODY>
</HTML>