Re: Managing a project as it scales

Tech-Archive recommends: Fix windows errors by optimizing your registry



See below...
On 17 Dec 2005 03:36:53 -0800, "Alexander" <the44secs@xxxxxxxxx> wrote:

>Thank you for taking the time to elaborate on these topics, Joe.
>
>The last few days have been very interesting. I've read a number of the
>articles on your site as well as others that I've found on the Code
>Project. I'm still processing all this information, but it is clear
>that I have made some obvious and not-so-obvious coding mistakes.
>
>One of the problem areas I have is in error handling, which I basically
>ignored (i.e. exceptions) or dealt with in a rather inconsistent
>manner. I can address the later but I still have difficulty
>understanding the appropriate use exceptions. I mean, even CString can
>throw an exception.
****
I once worked in a system where every function could throw an exception rather than return
an error code. It created a lot of work, and indeed code bloat was one of the downsides.
OTOH, when I was done, the application, which had previously had an MTBF of about 45
minutes, could now run 24/7 without failures. We ran it for six solid weeks before the
building power was lost, taking the entire mainframe down. So the degree of effort you
expend is often correlated to the needed reliability you require.
****
>If I were to try and catch all feasible exceptions
>the code would bloat enormously. Or worse, I could implement a
>"catch-all" and then not be able to spot legitimate errors while in
>development. So the questions is, when and where is a good idea to
>acknowledge exceptions? File operations?
***
Note that the basic dispatch loop for messages has a CATCH_ALL in it, so exceptions that
go flying back untrapped will get caught. In debug mode, at least, it pops up a dialog
box indicating that the exception has been caught.
>
>Another issue is the manipulation of controls from classes other than
>its owner (say, a CDialog derived class). I now realize I shouldn't
>pass pointers to controls or even to "this" and use SendMessage or, if
>from a thread, use PostMessage. The thing is, how do you identify the
>control?
****
You don't identify the control. You identify the operation you want done, and leave it up
to the dialog to implement the details by manipuatling one or more controls.
****
> It seems you should at least have access to a dialog/window
>handle (hwnd). Is that the appropriate method? Updating a progress bar
>or a static control from a thread is a fairly common task I implement.
>What's the correct approach?
****
As answered in another reply, you simply indicate the abstraction of what you want, and
leave the implementation details up to the recipient. When you send a notification to
update some information, you have to think in terms of the information you want to update.
How that information is associated with your control is nobody's business except the owner
of the control, which is the dialog. In some cases, you create the control with a method
that actually does the update, so the dialog just calls that method, saying "Here, do
whatever is appropriate with this information" and the control itself manages what is
going on. For example, in my essay "Who owsn the GUI", I point out that selections could
be done by either combo box or radio buttons, and options could be selected by either
check boxes or a checked-listbox. How they are represented is of no concern of the
requestor. You think of an abstraction: "Query the mumble option" or "Set the mumble
option". The implementation details are of no interest at all. Hence there is no need to
ever know what control is involved.
****
>
>I have also been subclassing to deeply and have ended up having to
>populate the base class with scores of virtual methods. My thinking was
>that intermediate classes would avoid duplicating code but I can see
>how they have complicated the implementation a lot. A rewrite is in
>order.
****
This is a very powerful methodology, but it is fraught with complexity. I've used it
myself, and ultimately regretted it, because I could no longer manage the complexity.

<<True story: How do you know when you've been working too hard? Answer: when you wake up
in the Intensive Care Unit after the second major surgery in 36 hours, having lost over
half your blood supply due to internal hemorrhage. There are machines nearby clunking
away filling you up with antibiotics; blood transfusions in progress; there is the haze of
post-anesthesia, plus a morphine drip to manage the incredible pain. In the midst of
this, you realize there's a tube up your nose keeping your stomach drained, and a catheter
keeping your bladder drained...and in the middle of all this, one thought springs
crystal-clear into your mind: "Goord grief, my input/output methods have been subclassed!"

The next 24 hours I blame on the painkillers. I was in a panic because I couldn't recall,
when I swallowed, if I was supposed to call the superclass Stomach::Swallow before or
after I swallowed...

The only thing reassuring was that if, under those extreme conditions, I could think of
the subclassing problem, I hadn't suffered any brain function decay due to the massive
blood loss.

But I think that's when I decided that deep subclassing was hazardous. I rarely subclass
virtual methods deeply these days...>>
****
>
>Documentation is an area where I have to start from scratch. I've
>looked around and have found some tools (say, Doxygen) but it seems
>that your commenting style does not follow a format I recognize. do you
>use a documentation tool? If so, which? If not, am I to understand they
>are not worth the effort?
****
It is a formatting style we developed around 1981. I don't use a documentation tool
because I'm not convinced that they deliver that much value-added. But by insisting that
every function have a header (and I should also point out that I put a formfeed before
each one, so if I ever print a listing, I get one function per page, except for functions
that are longer than a page. But I never get two functions per page, or a partial heading
at the bottom of one page with the rest on the next page. Due to terminal brain damage in
the design, VS does not allow the insertion of form feeds, although I cannot come up with
a single reason this should be so.

When I read client code that I'm having to reverse engineer, I reformat it to my standard
format, add the function headers, and this really forces me to read the code. I take
about 1 day for every 20K lines of source code.

Another thing I do is when I make a change is that every module, once it is considered
"done", gets a formal change log entry at the front. It looks like this:

/*****************************************************************************
* Change Log
* Date | Change
*-----------+-----------------------------------------------------------------
* 30-Sep-03 | Created
* 30-Oct-03 | REQ #223: Support new per-packet timeout
* 8-Nov-03 | REQ #281: Support per-packet timeout correctly
*****************************************************************************/

This is a particularly amusing entry. But the key is that with a single keystroke, I
create a new line in the change log, with the date. A second keystroke puts the the REQ#,
which indicates the change request coming in from the outside. So, for example, I created
the module on 30-Sep-03. On 30-Oct, as a result of a new design, change request #223, I
made changes in the file. I also introduced a bug, an a week or so later, I fixed the
bug. The bug report generated change request #281.

As we develop the product, adding features, fixing bugs, changing the interface, whatever,
each change generates a new change request (which is stored in a file called REQ.TXT,
because every project has a REQ.TXT). REQ.TXT looks something like this

223 JH New per-packet timeout algorithm implemented in firmware; update
application to match

224 JMN Added download-valid detection timeout based on new packet timeout

225 JMN Added recovery code to resynchronize after packet timeout

Now, every line of change that is involved in this change gets the REQ # at the end of the
line...

#define PACKET_TI MEOUT_INTERVAL 500 /* ms */ // REQ #223

SetTimer(TIMER_PACKET_TIMEOUT, PACKET_TIMEOUT_INTERVAL, NULL); // REQ #223

when I put these in, they actually go "flush right" to a particular column unless the line
is longer than that column. If I remove a line, I just // comment it out, and add the
//REQ comment at the end.

A given line may have several comments:

value = <<complex formula here>> // REQ #216 // REQ #232 // REQ #244

as changes are made on it.

This has several advantages. One is that I can run a report generator (actually, an awk
script), and extract each change log entry and a line count

REQ #200 | (9)
---------+
../CalibrationData.cpp: * 26-Oct-03 | REQ #200: jdh: Formatted ***** output to be %e
format
../CalibrationParamsPage.cpp: * 19-Oct-03 | REQ #200: change scale factor for spin control
on ******* and
../CalibrationParamsPage.cpp: * | 1 **** to .0001..jdh
../OutputsPage.cpp: * 19-Oct-03 | REQ #200: [jdh] Format change for more precision zeroing
*****
../OutputsPage.cpp: * | gauge
../*****View.cpp: * 19-Oct-03 | REQ #200: [jdh] Make ***** pressure always read in sci
notation

Note this looks a lot better in a fixed-width font (I don't know what font your newsgroup
reader is using. ***** blocks out some proprietary information)

REQ #200 involved 9 lines of changes in four modules, and involved using different display
formats for a particular gauge reading on various pages, depending on the purpose of the
page.

Among other things, this lets me track how many lines of code are involved in a change.
When a client says "Why did you spend K hours making that change?" I can quote the exact
number of lines of code it took to make the change. Some changes involve writing fairly
large modules. I had one change in a project that involved writing something like 8K
lines of code. For each project, I can also get summary statistics:

----------------------------------------------------------------------------
Total REQs 205
Change lines 7984
Total source lines 46145
Total files 244
----------------------------------------------------------------------------

This is from a fairly small project (only 46K source lines), but note that over time, 17%
of the lines are the results of bug reports, design changes, new feature additions, etc.

I don't believe in CVS-style checkin logging. The problem is that you can check out 20
modules, make changes over several days, and then go to check them in. It is too hard to
remember what is being fixed and why. The change logs are updated immediately as I make
changes in the modules, truly in realtime as I edit. With a powerful editor and
user-defined extensions, this is easy to make quite painless (I can't imagine having to
rewrite my entire editor interface into Visual Studio; I've got 20 years invested in
editor extensions). So when I check in to the source control system, I just record the
REQ numbers added since the checkout, which is easy to find.

While it is true that I can get change listings of source lines from any respectable
source management system, it only tells me that lines changed. It doesn't tell me why
they changed, or what fix, retrospectively, caused the lines to change. This is
particularly true when a whole set of loosely-related or unrelated fixes go into a
release. Looking back on it a year later it is hard to separate them out unless I leave a
trail of breadcrumbs. It also means that three days later, when the customer's internal
or external beta reports a problem, I know exactly what lines I've changed in the last
week, and what I thought they were doing.

Furthermore, the lsting I produce goes to the customer for tech support. They correlate
the REQ # values with release points. So when a customer calls up with a "I have the
following problem", they can check to see if this problem has been fixed, and if so, what
version and release it was fixed in. So they know if they've got a fix already done, and
don't have to waste a lot of time walking the customer through the problem.

Bottom line: it is a lot of little things that add up to an overall philosophy of
manageable code. I developed most of these techniques in the 1980s and I haven't found
anything more effective.
****
>
>Other questions pop to mind but I don't want to tax your patience.
>Thanks again.
>
>Alexander
Joseph M. Newcomer [MVP]
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.


Quantcast