问:
What are good ways to organize input files (Makefiles, SConstruct, CMakeLists.txt, etc.) to build automation software?
One thing I like to do with my code is make sure that it's refactored into manageable pieces. However, when it comes to building the software, I find that whatever build automation software I end up using (lately it's been GNU Make or SCons) ends up becoming a complete mess. The input files look like long scripts that seem to defy easy refactoring. I'd like to be able to refactor them in some way, but the concept of "function" doesn't quite behave the same way in some build automation software as it does in a programming language, so I find it difficult to write manageable Makefiles or SConscript files for any projects that are moderately complicated.

Does anyone have any advice on writing manageable input files for build automation software? Software-agnostic advice would be best, but even advice about a specific build automation tool would be helpful, particularly Make or SCons, since that's what I've been using for projects.

Edit: As Thorbjørn has pointed out, I should add some context and usage examples. I'm finishing up my PhD in chemical engineering and I do research in computational science. (I'm a pro tem mod over at SciComp.SE, for those of you who visit.) My projects typically involve a mix of compiled languages (C, C++, Fortran) that do some of the heavy lifting, scripting languages (Python, Perl) for prototyping, and occasionally, domain-specific languages for prototyping or technical purposes.

I've added two examples below, roughly in the 250-line range. The problem for me is generally a lack of modularity. Some of these projects can be organized into modular units, and it'd be nice to abstract the parts of the build along those lines to make it easier for me and future maintainers to keep track of. Breaking each script up into multiple files has been one solution I've been toying around with in my head.

The second example is particularly important, because I'm going to have to a large number of files to it soon.

Here's what a 265-line Makefile might look like for me, taken from an actual project, and organized as best I can:

#!/usr/bin/make
#Directory containing DAEPACK library folder
daepack_root = .
library = $(daepack_root)/lib
wrappers = $(daepack_root)/Wrappers/DSL48S
c_headers = parser.h problemSizes.h
f77_headers=problemSizes.f commonParam.f
f90_headers=problemSizes.f commonParam.f90
includes = -I. -Iinclude -I/usr/include/glib-2.0 \
    -I/usr/lib/glib-2.0/include -I/usr/include/libxml2 \
    -I/usr/include/libgdome -I/usr/include/gtest/

#Fortran 77 environment variables
f77=gfortran
fflags=-ggdb -cpp -fno-second-underscore --coverage -falign-commons \
    -mcmodel=large -fbacktrace -pg
flibs=

#Fortran 90 environment variables
f90=gfortran
f90flags=-ggdb -cpp -fno-second-underscore --coverage -falign-commons \
    -mcmodel=large -fbacktrace -pg
f90libs=

#C environment flags
cc=gcc
cflags=-ggdb --coverage $(includes) -mcmodel=large
clibs=

#Libraries for linking
libs=-L$(library) -ldaepack_sparse -lblas -llapack -ldl -lg2c \
    -lgdome -lxml2 -lgtest -lcunit -lcholmod -lamd -lcolamd -lccolamd \
    -lmetis -lspqr -lm -lblas -llapack -lstdc++ -lpcre

#Object files
objs=main.o $(dsl48sObjs) $(gdxObjs)
gdxObjs = gdxf9def.o gdxf9glu.o gamsglobals_mod.o
commonObjs=libdsl48s_model.sl cklib.o parser.o $(gdxObjs)
originalModelObjs=originalModel.o dsl48sChemkinModule.o $(commonObjs)
cspSlowModelObjs=cspSlowModel.o dsl48sChemkinModuleSlow.o cspModule.o \
    $(commonObjs)
orthoProjModelObjs=orthoProjModel.o dsl48sChemkinModuleOrthoProj.o \
    orthoProjModule.o basisModule.o spqrUtility.o $(commonObjs)

#Shell environment variable definitions for FUnit
FCFLAGS := $(f90flags)
LDFLAGS := libdsl48s_model.sl cklib.o gdxf9glu.o parser.o spqrUtility.o \
    $(libs)

misc=*table *size.f
output=*.out

#Ftncheck flags for static analysis of Fortran 77 code
ftnchekflags= -declare -include=. -library -style=block-if,distinct-do,do-enddo,end-name,goto,labeled-stmt,structured-end

all: ckinterp.exe parserTest.exe originalModel.exe cspSlowModel.exe \
    orthoProjModel.exe spqrUtilityTest.exe
#Check code style with lexical analyzer
    @echo Checking program style...
    ftnchek $(ftnchekflags) rhs.f
    ftnchek $(ftnchekflags) resorig.f
    ftnchek $(ftnchekflags) res.f
#   ftnchek $(ftnchekflags) cklib.f
#   ftnchek $(ftnchekflags) ckinterp.f
#Set up baseline coverage data file
    @echo Set up baseline coverage data file
    lcov -c -i -d . -o conpDSL48Sbase.info
#Run unit test on cspModule.f90
    @echo Running unit tests on cspModule.f90...
    funit cspModule
#Generate test coverage data for cspModule.f90
    @echo Generating test coverage data from cspModule.f90 tests...
    lcov -c -d . -o conpDSL48ScspTest.info
#Run unit test on orthoProjModule.f90
    @echo Running unit tests on orthoProjModule.f90...
    funit orthoProjModule
#Generate test coverage data for orthoProjModule.f90
    @echo Generating test coverage data from orthoProjModule.f90 tests...
    lcov -c -d . -o conpDSL48SgenProjTest.info
#Run unit tests on the parser C library
    @echo Running unit tests on parser in C...
    -G_SLICE=always-malloc G_DEBUG=gc-friendly valgrind -v --tool=memcheck \
    --leak-check=full --show-reachable=yes --leak-resolution=high \
    --num-callers=20 --log-file=parserTest.vgdump \
    ./parserTest.exe > parserTest.log
#Generate test coverage data for the parser wrapper C library
    @echo Generating test coverage data for the parser in C...
    lcov -c -d . -o conpDSL48SparserTest.info
#Run unit tests on the SparseQR C library
    @echo Running unit tests on SparseQR library in C...
    ./spqrUtilityTest.exe
#Generate test coverage data for the SparseQR C library
    @echo Generating test coverage data for the SparseQR C library...
    lcov -c -d . -o conpDSL48SsparseTest.info
#Run unit test on basisModule.f90
    @echo Running unit tests on basisModule.f90...
    funit basisModule
#Generate test coverage data for basisModule.f90
    @echo Generating test coverage data from basisModule.f90 tests...
    lcov -c -d . -o conpDSL48SbasisMod.info
#Combine test coverage data
    @echo Combine baseline and test coverage data...
    lcov -a conpDSL48Sbase.info \
    -a conpDSL48ScspTest.info \
    -a conpDSL48SgenProjTest.info \
    -a conpDSL48SbasisMod.info \
    -a conpDSL48SparserTest.info \
    -a conpDSL48SsparseTest.info \
    -o conpDSL48Stotal.info
#Post-process to remove coverage statistics from automatically
#generated source code.
    @echo Removing coverage statistics for automatically generated source...
    lcov -r conpDSL48Stotal.info basisModule_fun.f90 \
    ckinterp.f cklib.f cspModule_fun.f90 davisSkodjeAd.f90 \
    davisSkodjeJac.f90 davisSkodjeRes.f90 davisSkodjeRhs.f90 \
    davisSkodjeSp.f90 gdxf9def.f90 gdxf9glu.c orthoProjModule_fun.f90 \
    jac.f jacorig.f resad.f resadp.f resorigad.f resorigadp.f ressp.f \
    resorigsp.f senrhs.f senrhsorig.f TestRunner.f90 \
    -o conpDSL48Stotal.info
#Generate HTML report of coverage data
    @echo Generate HTML report of coverage data...
    genhtml conpDSL48Stotal.info
    @echo Open "index.html" in browser for coverage results!

originalModel.exe: $(originalModelObjs) $(f90_headers) $(f77_headers) \
    $(c_headers)
    $(f90) $(f90flags) -o originalModel.exe $(originalModelObjs) $(libs)

originalModel.o: dsl48sChemkinModule.o $(commonObjs) $(f77_headers) \
    $(f90_headers) $(c_headers)
    $(f90) $(f90flags) -c -o originalModel.o originalModel.f90

cspSlowModel.exe: $(cspSlowModelObjs) $(f90_headers) $(f77_headers) \
    $(c_headers)
    $(f90) $(f90flags) -o cspSlowModel.exe $(cspSlowModelObjs) $(libs)

cspSlowModel.o: dsl48sChemkinModuleSlow.o cspModule.o $(commonObjs) \
    $(c_headers) $(f77_headers)
    $(f90) $(f90flags) -c -o cspSlowModel.o cspSlowModel.f90

orthoProjModel.exe: $(orthoProjModelObjs) $(f90_headers) $(f77_headers) \
    $(c_headers) resOrthoFast.o
    $(f90) $(f90flags) -o orthoProjModel.exe $(orthoProjModelObjs) \
    resOrthoFast.o $(libs)

orthoProjModel.o: dsl48sChemkinModuleOrthoProj.o orthoProjModule.o $(commonObjs) \
    $(c_headers) $(f90_headers) $(f77_headers) resOrthoFast.o basisModule.o
    $(f90) $(f90flags) -c -o orthoProjModel.o orthoProjModel.f90

dsl48sChemkinModule.o: dsl48sChemkinModule.f90 cklib.o problemSizes.h \
    parser.o $(c_headers) $(f90_headers)
    $(f90) $(f90flags) -c -o dsl48sChemkinModule.o dsl48sChemkinModule.f90

dsl48sChemkinModuleSlow.o: dsl48sChemkinModuleSlow.f90 cspModule.o cklib.o \
    problemSizes.h parser.o $(c_headers) $(f90_headers)
    $(f90) $(f90flags) -c -o dsl48sChemkinModuleSlow.o \
    dsl48sChemkinModuleSlow.f90

dsl48sChemkinModuleOrthoProj.o: dsl48sChemkinModuleOrthoProj.f90 \
    orthoProjModule.o basisModule.o cklib.o problemSizes.h \
    parser.o $(c_headers) $(f90_headers)
    $(f90) $(f90flags) -c -o dsl48sChemkinModuleOrthoProj.o \
    dsl48sChemkinModuleOrthoProj.f90

basisModule.o: basisModule.f90 cklib.o spqrUtility.o commonParam.f90
    $(f90) $(f90flags) -c -o basisModule.o basisModule.f90

spqrUtility.o: spqrUtility.h spqrUtility.c
    $(cc) $(cflags) -c -o spqrUtility.o spqrUtility.c

spqrUtilityTest.exe: spqrUtilityTest.o spqrUtility.o
    $(cc) $(cflags) -o spqrUtilityTest.exe spqrUtilityTest.o \
    spqrUtility.o $(libs)

spqrUtilityTest.o: spqrUtilityTest.c spqrUtility.o
    $(cc) $(cflags) -c -o spqrUtilityTest.o spqrUtilityTest.c

cklib.o: cklib.f ckstrt.f
    $(f77) $(fflags) -c -o cklib.o cklib.f

ckinterp.exe: ckinterp.o
    $(f77) $(fflags) -o ckinterp.exe ckinterp.o

ckinterp.o: ckinterp.f
    $(f77) $(fflags) -c -o ckinterp.o ckinterp.f

#Recursive makefile inherited from previous graduate students
libdsl48s_model.sl: $(f77_headers) cklibDAEPACK.f
    cp $(wrappers)/makefile model.mk
    make -f model.mk

resOrthoFast.o: libdsl48s_model.sl
    $(f90) $(f90flags) -c -o resOrthoFast.o resOrthoFast.f90

problemSizes.f: problemSizes.fpp problemSizes.h
    cpp problemSizes.fpp problemSizes.f
    perl -p -i.bak -we 's/# /! /;' problemSizes.f

commonParam.f90: commonParam.f
    perl -p -i.bak -we 's/^#/!/;' commonParam.f
    echo "commonParam t f t fpp" | pref77tof90
    echo "commonParam /" | f77tof90
    perl -p -i.bak -we 's/integer a/!integer a/;' commonParam.f
    perl -p -i.bak -we 's/END   //;' commonParam.f90

commonParam.f: commonParam.fpp problemSizes.h
    cpp commonParam.fpp commonParam.f
    perl -p -i.bak -we 's/^#/!/;' commonParam.f

cspModule.o: cspModule.f90
    $(f90) $(f90flags) -c -o cspModule.o cspModule.f90

orthoProjModule.o: gamsglobals_mod.o gdxf9def.o gdxf9glu.o orthoProjModule.f90 \
    formatLabels.f90
    $(f90) $(f90flags) -c -o orthoProjModule.o orthoProjModule.f90

gdxf9def.o: gdxf9def.f90
    $(f90) $(f90flags) -c -o gdxf9def.o gdxf9def.f90

gdxf9glu.o: gdxf9glu.c gdxf9def.o
#64-bit version of wrappers (with underscores)
    $(cc) $(cflags) -DCIA_LEX -DAPIWRAP_LCASE_DECOR -c -o \
    gdxf9glu.o gdxf9glu.c
#64-bit version of wrappers (without underscores, for C interoperability)
#   $(cc) $(cflags) -DCIA_LEX -DAPIWRAP_LCASE_NODECOR -c gdxf9glu.c
#32-bit version of wrappers
#   $(cc) $(cflags) -DAPIWRAP_LCASE_DECOR -c gdxf9glu.c -Iinclude

gamsglobals_mod.o: gamsglobals_mod.f90 gdxf9def.o gdxf9glu.o
    $(f90) $(f90flags) -c gamsglobals_mod.f90

parser.o: parser.c $(c_headers)
    $(cc) $(cflags) -c -o parser.o parser.c

parserTest.exe: parserTest.o parser.o
    $(cc) $(cflags) -o parserTest.exe parser.o \
    parserTest.o $(libs)

parserTest.o: parserTest.cpp parser.o
    $(cc) $(cflags) -c -o parserTest.o parserTest.cpp

clean:
    -rm *.bak
    -rm *.f77
    -rm *.log
    -rm commonParam.f90
    -rm problemSizes.f
    -rm commonParam.f
    -make clean -f model.mk
    -rm model.mk
    -rm *.o
    -rm *.mod
    -rm $(misc)
    -rm *.exe
    -funit --clean
    -rm *.gcno
    -rm *.gcda
    -rm *.info
    -rm *.png
    -rm *.html
    -rm *.css
    -rm -rf html
    -rm *.pyc
    -rm *.lst
```

<p>Here's a 245-line <code>SConstruct</code> file that I'm currently trying to organize for a project that's roughly as complex:</p>

<pre class="lang-py prettyprint-override"><code>## \file SConstruct
#  \brief Compiles the library and compiles tests.
#

import SCons

## \brief Build up directory names of each COIN library from package names
#         and versions.
#

## Overall SCons environment
#
env = Environment();

flags = []

## Compile using debug versions?
#
debug = True
debugString = '-debug'
debugFlags = ['-ggdb']

dynamicLinkFlag = '-Wl,-rpath,'

if debug:
    flags += debugFlags

## Compile Google Test from scratch.
#
GTestVersion = '1.6.0'
GTestStem = 'gtest-' + GTestVersion
GTestBuildIncDir = [GTestStem,
                    GTestStem + '/include',
                    ]
GTestAllLib = env.Library('lib/libgtest.a', 'gtest-1.6.0/src/gtest-all.cc',
                      CPPPATH = GTestBuildIncDir,
                      CXXFLAGS = flags)
GTestMainLib = env.Library('lib/libgtest_main.a',
                           'gtest-1.6.0/src/gtest_main.cc',
                           CPPPATH = GTestBuildIncDir,
                           CXXFLAGS = flags)

GTestIncDir = GTestStem + '/include/gtest'
GTestLibDir = 'lib'
GTestLibFlags = ['gtest', 'gtest_main', 'pthread']

## Armadillo matrix library
#
ArmadilloLibFlags = ['armadillo'];

## Quick reminder of SCons flags:
#  CPPPATH = path of headers (include directories)
#  LIBPATH = path of libraries
#  LIBS = flags of libraries
#  CXXFLAGS = C++ compilation flags
#

## Locations of libraries installed on system in standard locations
#
StdIncDir = '/usr/include'
StdLibDir = '/usr/lib'

## Configuration information for COIN libraries
#
CoinUtilsVersion = '2.6.4'
ClpVersion = '1.12.0'
OsiVersion = '0.103.0'
CbcVersion = '2.5.0'

## Some standard directory locations of COIN libraries, with slashes added for
#  for convenience.
#
CoinLibLocation = '/usr/local/COIN/'
StdCoinIncDir = '/include/coin'
StdCoinLibDir = '/lib'

CoinUtilsStem = 'CoinUtils-' + CoinUtilsVersion
ClpStem = 'Clp-' + ClpVersion
OsiStem = 'Osi-' + OsiVersion
CbcStem = 'Cbc-' + CbcVersion

if debug:
    CoinUtilsStem += debugString
    CbcStem += debugString
    ClpStem += debugString
    OsiStem += debugString

## Build up include directory names for COIN projects from constituent parts.
#
CoinUtilsIncDir = CoinLibLocation + CoinUtilsStem + StdCoinIncDir
ClpIncDir = CoinLibLocation + ClpStem + StdCoinIncDir
OsiIncDir = CoinLibLocation + OsiStem + StdCoinIncDir
CbcIncDir = CoinLibLocation + CbcStem + StdCoinIncDir

## Build up library names from COIN projects from constituent parts
#
CoinUtilsLibDir = CoinLibLocation + CoinUtilsStem + StdCoinLibDir
ClpLibDir = CoinLibLocation + ClpStem + StdCoinLibDir
OsiLibDir = CoinLibLocation + OsiStem + StdCoinLibDir
CbcLibDir = CoinLibLocation + CbcStem + StdCoinLibDir

## CPLEX
#
CpxStem = '/opt/ibm/ILOG/CPLEX_Studio_Academic123/cplex/'
CpxIncDir = CpxStem + 'include/ilcplex'
CpxLibDir = CpxStem + 'lib/x86-64_sles10_4.1/static_pic'

## Gurobi
#
GrbStem = '/opt/gurobi460/linux64/'
GrbIncDir = GrbStem + 'include'
GrbLibDir = GrbStem + 'lib'

OsiLibFlags = ['Osi', 'CoinUtils']
ClpLibFlags = ['Clp', 'OsiClp']
CbcLibFlags = ['Cbc', 'Cgl']
OsiCpxLibFlags = ['OsiCpx']
OsiGrbLibFlags = ['OsiGrb']
CpxLibFlags = ['cplex', 'ilocplex', 'pthread', 'm']
GrbLibFlags = ['gurobi_c++', 'gurobi46', 'pthread', 'm']

milpIncDirs = [CoinUtilsIncDir,
               ClpIncDir,
               OsiIncDir,
               CbcIncDir,
               CpxIncDir,
               GrbIncDir,
            GTestIncDir,
               ]
milpLibDirs = [CoinUtilsLibDir,
               ClpLibDir,
               OsiLibDir,
               CbcLibDir,
               CpxLibDir,
               GrbLibDir,
               GTestLibDir,
            ]
milpLibFlags = [OsiCpxLibFlags,
                OsiGrbLibFlags,
                CbcLibFlags,
                ClpLibFlags,
                OsiLibFlags,
                CpxLibFlags,
                GrbLibFlags,
                GTestLibFlags,
                ]
##milpSolver = env.Object('milpSolver.cpp',
            ##                         CPPPATH = milpIncDirs,
##                         LIBPATH = milpLibDirs,
##                         CXXFLAGS = flags)
milpSolverTest = env.Program('milpSolverUnitTest',
                                  ['milpSolverTest.cpp',
                                   'milpSolver.cpp'],
                                  CPPPATH = milpIncDirs,
                                  LIBPATH = milpLibDirs,
                                  LIBS = milpLibFlags,
                                  CXXFLAGS = flags,
                                  LINKFLAGS = ['-Wl,-rpath,' + OsiLibDir])
env.Depends(milpSolverTest, [GTestAllLib, GTestMainLib])

## Chemkin source directories and files
#
ChemkinSourceDir = '/mnt/hgfs/DataFromOldLaptop/Data/ModelReductionResearch/Papers/AdaptiveChemistryPaper/AdaptiveChemistry/NonOpenSource/ChemkinII/';
ChemkinSourceList = ['cklib.f', 'pcmach.f','tranlib.f']
ChemkinSourceList = [ChemkinSourceDir + FileName
                     for FileName in ChemkinSourceList]
env.Depends('cklib.f','ckstrt.f')

## Cantera include directorie
#
CanteraStem = '/usr/local/cantera'

if debug:
    CanteraStem += debugString

CanteraIncDir = CanteraStem + '/include/cantera'
CanteraLibDir = CanteraStem + '/lib'
CanteraTestingFlags = ['kinetics', 'thermo', 'tpx', 'ctbase', 'm',]
CanteraLibFlags = ['user', 'oneD', 'zeroD', 'equil', 'kinetics', 'transport',
                    'thermo', 'ctnumerics', 'ctmath', 'tpx', 'ctspectra',
                    'converters', 'ctbase', 'cvode', 'ctlapack', 'ctblas',
                    'ctf2c', 'ctcxx', 'ctf2c', 'm', 'm', 'stdc++']

CxxFortranFlags = ['g2c', 'gfortran'];

chemSolverIncDir = [CanteraIncDir,
                    StdIncDir,
                    '/usr/local/include',
                    GTestIncDir,
                    ]
chemSolverLibDir = [StdLibDir,
                    CanteraLibDir,
                    GTestLibDir,
                    ]
chemSolverLibFlags = [GTestLibFlags,
                      CxxFortranFlags,
                      CanteraLibFlags,
                      ArmadilloLibFlags,
                      ]

chemSolverTest = env.Program('chemSolverUnitTest',
                        ['chemSolverTest.cpp',
                         'chemSolver.cpp',
                         'ckwrapper.f90'] + ChemkinSourceList,
                        CPPPATH = chemSolverIncDir,
                        LIBPATH = chemSolverLibDir,
                        LIBS = chemSolverLibFlags,
                        CXXFLAGS = flags,
                        FORTRANFLAGS = flags,
                        F90FLAGS = flags)
env.Depends(chemSolverTest, [GTestAllLib, GTestMainLib])

#env.AddPostAction(milpSolverTest, milpSolverTest[0].abspath)
testAlias = env.Alias('test', [milpSolverTest, chemSolverTest])
AlwaysBuild(testAlias)

ckInterp = env.Program('ckinterp', ChemkinSourceDir + 'ckinterp.f')

canteraGTestLibFlags = CanteraTestingFlags + GTestLibFlags

#canteraGTestLibFlags = ['kinetics', 'thermo', 'tpx',
#                        'ctbase',  'm', 'gtest', 'gtest_main', 'pthread']

canteraGTest = env.Program('canteraGTest',
                           'canteraGTest.cpp',
                           CPPPATH = chemSolverIncDir,
                           LIBPATH = chemSolverLibDir,
                           LIBS = canteraGTestLibFlags,
                           CXXFLAGS = flags)
env.Depends(canteraGTest, [GTestAllLib, GTestMainLib])

canteraMemTestLibFlags = CanteraTestingFlags

canteraMemTest = env.Program('canteraMemTest',
                             'canteraMemTest.cpp',
                             CPPPATH = chemSolverIncDir,
                             LIBPATH = chemSolverLibDir,
                             LIBS = canteraMemTestLibFlags,
                             CXXFLAGS = flags)
</code>```

答:
Refactoring strategy

In addition to the comments by dietbuddha and ThorbjørnRavnAnderson, another way to refactor build scripts is to separate them into multiple files. How you do this depends on the build system.

For Make, it's as simple as using the include command, as recommended in "Recursive Make Considered Harmful". This directive works just like #include in the C preprocessor and processes the included file as if it had been cut and pasted in place of the include command. Using the include command, it's possible to refactor your main Makefile by moving modular pieces into sub-Makefiles.

CMake has a similar command.

SCons requires a similar type of approach with different commands. The basic idea of splitting up the build script into a master script and several smaller sub-scripts stays the same, but instead of directly including the text of the smaller scripts in the master build script, SCons treats the smaller scripts like separate namespaces (because SCons uses Python instead of the shell). SCons Environment objects have a method called SConscript() that enables you to import objects from an SConstruct file to subsidiary files called SConscript files that can be used to refactor your build script. The basic idea of SConscript files and the SConscript() command can be found here on the SCons Wiki. Examples of how to use SConscripts in hierarchical builds can be found here in the SCons User Guide.

Extrapolating from these three examples, it seems like the general strategy is to refactor a build script by splitting it into a master script that calls several files. How one does this is idiomatic to the particular build automation software used.

SCons Example, Revisited

Taking the SConstruct file above, I moved all of the configuration information into a module called build_config.py. All literals live in the global namespace, which could be dangerous for Python, but that's easily (though somewhat tediously) fixable. I checked ahead to make sure that I had no name clashes with __builtin__ (the Python built-in namespace, so I'm not overwriting any important objects).

## \file build_config.py
#  \brief Sets configuration of file locations manually.
#

## Flags for compilers
#

flags = []

## Compile using debug versions?
#
debug = True
debugString = '-debug'
debugFlags = ['-ggdb']

dynamicLinkFlag = '-Wl,-rpath,'

if debug:
    flags += debugFlags

## Configuration information for GTest
#
GTestVersion = '1.6.0'
GTestStem = 'gtest-' + GTestVersion
GTestBuildIncDir = [GTestStem,
                    GTestStem + '/include',
                    ]

GTestIncDir = GTestStem + '/include/gtest'
GTestLibDir = 'lib'
GTestLibFlags = ['gtest', 'gtest_main', 'pthread']

## Configuration information for Armadillo matrix library
#

ArmadilloLibFlags = ['armadillo'];

## Locations of libraries installed on system in standard locations
#
StdIncDir = '/usr/include'
StdLibDir = '/usr/lib'

## Configuration information for COIN libraries
#
CoinUtilsVersion = '2.6.4'
ClpVersion = '1.12.0'
OsiVersion = '0.103.0'
CbcVersion = '2.5.0'

## Standard directory locations of COIN libraries, with slashes added for
#  for convenience.
#
CoinLibLocation = '/usr/local/COIN/'
StdCoinIncDir = '/include/coin'
StdCoinLibDir = '/lib'

CoinUtilsStem = 'CoinUtils-' + CoinUtilsVersion
ClpStem = 'Clp-' + ClpVersion
OsiStem = 'Osi-' + OsiVersion
CbcStem = 'Cbc-' + CbcVersion

if debug:
    CoinUtilsStem += debugString
    CbcStem += debugString
    ClpStem += debugString
    OsiStem += debugString

## Build up include directory names for COIN projects from constituent parts.
#
CoinUtilsIncDir = CoinLibLocation + CoinUtilsStem + StdCoinIncDir
ClpIncDir = CoinLibLocation + ClpStem + StdCoinIncDir
OsiIncDir = CoinLibLocation + OsiStem + StdCoinIncDir
CbcIncDir = CoinLibLocation + CbcStem + StdCoinIncDir

## Build up library names from COIN projects from constituent parts
#
CoinUtilsLibDir = CoinLibLocation + CoinUtilsStem + StdCoinLibDir
ClpLibDir = CoinLibLocation + ClpStem + StdCoinLibDir
OsiLibDir = CoinLibLocation + OsiStem + StdCoinLibDir
CbcLibDir = CoinLibLocation + CbcStem + StdCoinLibDir

## CPLEX
#
CpxStem = '/opt/ibm/ILOG/CPLEX_Studio_Academic123/cplex/'
CpxIncDir = CpxStem + 'include/ilcplex'
CpxLibDir = CpxStem + 'lib/x86-64_sles10_4.1/static_pic'

## Gurobi
#
GrbStem = '/opt/gurobi460/linux64/'
GrbIncDir = GrbStem + 'include'
GrbLibDir = GrbStem + 'lib'

OsiLibFlags = ['Osi', 'CoinUtils']
ClpLibFlags = ['Clp', 'OsiClp']
CbcLibFlags = ['Cbc', 'Cgl']
OsiCpxLibFlags = ['OsiCpx']
OsiGrbLibFlags = ['OsiGrb']
CpxLibFlags = ['cplex', 'ilocplex', 'pthread', 'm']
GrbLibFlags = ['gurobi_c++', 'gurobi46', 'pthread', 'm']

milpIncDirs = [CoinUtilsIncDir,
               ClpIncDir,
               OsiIncDir,
               CbcIncDir,
               CpxIncDir,
               GrbIncDir,
            GTestIncDir,
               ]
milpLibDirs = [CoinUtilsLibDir,
               ClpLibDir,
               OsiLibDir,
               CbcLibDir,
               CpxLibDir,
               GrbLibDir,
               GTestLibDir,
            ]
milpLibFlags = [OsiCpxLibFlags,
                OsiGrbLibFlags,
                CbcLibFlags,
                ClpLibFlags,
                OsiLibFlags,
                CpxLibFlags,
                GrbLibFlags,
                GTestLibFlags,
                ]

## Configuration information for Chemkin source directories and files
#
ChemkinSourceDir = '/mnt/hgfs/DataFromOldLaptop/Data/ModelReductionResearch/Papers/AdaptiveChemistryPaper/AdaptiveChemistry/NonOpenSource/ChemkinII/';
ChemkinSourceList = ['cklib.f', 'pcmach.f','tranlib.f']
ChemkinSourceList = [ChemkinSourceDir + FileName
                     for FileName in ChemkinSourceList]

## Configuration information for Cantera
#
CanteraStem = '/usr/local/cantera'

if debug:
    CanteraStem += debugString

CanteraIncDir = CanteraStem + '/include/cantera'
CanteraLibDir = CanteraStem + '/lib'
CanteraTestingFlags = ['kinetics', 'thermo', 'tpx', 'ctbase', 'm',]
CanteraLibFlags = ['user', 'oneD', 'zeroD', 'equil', 'kinetics', 'transport',
                    'thermo', 'ctnumerics', 'ctmath', 'tpx', 'ctspectra',
                    'converters', 'ctbase', 'cvode', 'ctlapack', 'ctblas',
                    'ctf2c', 'ctcxx', 'ctf2c', 'm', 'm', 'stdc++']

CxxFortranFlags = ['g2c', 'gfortran'];

chemSolverIncDir = [CanteraIncDir,
                    StdIncDir,
                    '/usr/local/include',
                    GTestIncDir,
                    ]
chemSolverLibDir = [StdLibDir,
                    CanteraLibDir,
                    GTestLibDir,
                    ]
chemSolverLibFlags = [GTestLibFlags,
                      CxxFortranFlags,
                      CanteraLibFlags,
                      ArmadilloLibFlags,
                      ]
canteraGTestLibFlags = CanteraTestingFlags + GTestLibFlags

#canteraGTestLibFlags = ['kinetics', 'thermo', 'tpx',
#                        'ctbase',  'm', 'gtest', 'gtest_main', 'pthread']
```

<p>The main <code>SConstruct</code> file calls a bunch of <code>SConscript</code> files to build modules containing the major features I have in my code. Moving most of the build commands to <code>SConscript</code> files makes the <code>SConstruct</code> file really simple:</p>

<pre class="lang-py prettyprint-override"><code>## \file SConstruct
#  \brief Compiles the library and compiles tests.
#

import SCons
from build_config import *

## \brief Build up directory names of each COIN library from package names
#         and versions.
#

## Overall SCons environment
#
env = Environment();

## Compile Google Test from source using SConscript file.
#
GTestAllLib, GTestMainLib = env.SConscript('gtest.scons',
                                           exports=['env'])

## Compile MILP solver module and tests from source using SConscript file.
#
milpSolverTest = env.SConscript('milpSolver.scons',
                                exports=['env'])

## Compile chemistry solver module and associated tests from source
#  using SConscript file.
chemSolverTest, canteraGTest = env.SConscript('chemSolver.scons',
                                              exports=['env'])

## Since all tests use GTest, make the dependency of the module
# tests on the GTest libraries explicit.
env.Depends(milpSolverTest, [GTestAllLib, GTestMainLib])
env.Depends(chemSolverTest, [GTestAllLib, GTestMainLib])
env.Depends(canteraGTest, [GTestAllLib, GTestMainLib])

#env.AddPostAction(milpSolverTest, milpSolverTest[0].abspath)
testAlias = env.Alias('test', [milpSolverTest, chemSolverTest])
AlwaysBuild(testAlias)
</code>```

Then all of three SConscript files have the extension .scons and break up the project into modules representing different functionality.

Google Test SConscript file:

## \file gtest.scons
#  \brief SConscript file that contains information for SCons build of
#  GTest. Use Python syntax highlighting for source.

from build_config import *
Import('env')

## Compile Google Test from scratch.
#
GTestAllLib = env.Library('lib/libgtest.a', 'gtest-1.6.0/src/gtest-all.cc',
                      CPPPATH = GTestBuildIncDir,
                      CXXFLAGS = flags)
GTestMainLib = env.Library('lib/libgtest_main.a',
                           'gtest-1.6.0/src/gtest_main.cc',
                           CPPPATH = GTestBuildIncDir,
                           CXXFLAGS = flags)

Return('GTestAllLib', 'GTestMainLib')
```

<p>Mixed-integer linear programming solver <code>SConscript</code> file:</p>

<pre class="lang-py prettyprint-override"><code>## \file milpSolver.scons
#  \brief SConscript file that contains information for SCons build of
#  mixed-integer linear programming solver module. Use Python syntax
#  highlighting for source.

from build_config import *
Import('env')

## Compile MILP solver module and tests.
#

##milpSolver = env.Object('milpSolver.cpp',
            ##                         CPPPATH = milpIncDirs,
##                         LIBPATH = milpLibDirs,
##                         CXXFLAGS = flags)
milpSolverTest = env.Program('milpSolverUnitTest',
                                  ['milpSolverTest.cpp',
                                   'milpSolver.cpp'],
                                  CPPPATH = milpIncDirs,
                                  LIBPATH = milpLibDirs,
                                  LIBS = milpLibFlags,
                                  CXXFLAGS = flags,
                                  LINKFLAGS = ['-Wl,-rpath,' + OsiLibDir])

Return('milpSolverTest')
</code>```

Chemistry engine SConscript file:

## \file chemSolver.scons
#  \brief  SConscript file that sets up SCons build of chemistry solver module.
#  Use Python syntax highlighting for source.

from build_config import *
Import('env')

## Compile CHEMKIN interpreter.
#
ckInterp = env.Program('ckinterp', ChemkinSourceDir + 'ckinterp.f')

## Enforce explicit dependence of CHEMKIN library on CHEMKIN
#  parameter file 'ckstrt.f' because SCons' scanner won't pick it up.
env.Depends('cklib.f','ckstrt.f')

chemSolverTest = env.Program('chemSolverUnitTest',
                        ['chemSolverTest.cpp',
                         'chemSolver.cpp',
                         'ckwrapper.f90'] + ChemkinSourceList,
                        CPPPATH = chemSolverIncDir,
                        LIBPATH = chemSolverLibDir,
                        LIBS = chemSolverLibFlags,
                        CXXFLAGS = flags,
                        FORTRANFLAGS = flags,
                        F90FLAGS = flags)

canteraGTest = env.Program('canteraGTest',
                           'canteraGTest.cpp',
                           CPPPATH = chemSolverIncDir,
                           LIBPATH = chemSolverLibDir,
                           LIBS = canteraGTestLibFlags,
                           CXXFLAGS = flags)

canteraMemTestLibFlags = CanteraTestingFlags

canteraMemTest = env.Program('canteraMemTest',
                             'canteraMemTest.cpp',
                             CPPPATH = chemSolverIncDir,
                             LIBPATH = chemSolverLibDir,
                             LIBS = canteraMemTestLibFlags,
                             CXXFLAGS = flags)

Return('chemSolverTest', 'canteraGTest')
```

The combination of these five files is probably a bit longer than the original long file, but it's easier for me to manage because I can separate the build into a configuration file, and a bunch of mostly uncoupled units, so I don't have to keep track of the whole build in my mind at one time.

Body:
Refactoring strategy

In addition to the comments by dietbuddha and ThorbjørnRavnAnderson, another way to refactor build scripts is to separate them into multiple files. How you do this depends on the build system.

For Make, it's as simple as using the include command, as recommended in "Recursive Make Considered Harmful". This directive works just like #include in the C preprocessor and processes the included file as if it had been cut and pasted in place of the include command. Using the include command, it's possible to refactor your main Makefile by moving modular pieces into sub-Makefiles.

CMake has a similar command.

SCons requires a similar type of approach with different commands. The basic idea of splitting up the build script into a master script and several smaller sub-scripts stays the same, but instead of directly including the text of the smaller scripts in the master build script, SCons treats the smaller scripts like separate namespaces (because SCons uses Python instead of the shell). SCons Environment objects have a method called SConscript() that enables you to import objects from an SConstruct file to subsidiary files called SConscript files that can be used to refactor your build script. The basic idea of SConscript files and the SConscript() command can be found here on the SCons Wiki. Examples of how to use SConscripts in hierarchical builds can be found here in the SCons User Guide.

Extrapolating from these three examples, it seems like the general strategy is to refactor a build script by splitting it into a master script that calls several files. How one does this is idiomatic to the particular build automation software used.

SCons Example, Revisited

Taking the SConstruct file above, I moved all of the configuration information into a module called build_config.py. All literals live in the global namespace, which could be dangerous for Python, but that's easily (though somewhat tediously) fixable. I checked ahead to make sure that I had no name clashes with __builtin__ (the Python built-in namespace, so I'm not overwriting any important objects).

## \file build_config.py
#  \brief Sets configuration of file locations manually.
#

## Flags for compilers
#

flags = []

## Compile using debug versions?
#
debug = True
debugString = '-debug'
debugFlags = ['-ggdb']

dynamicLinkFlag = '-Wl,-rpath,'

if debug:
    flags += debugFlags

## Configuration information for GTest
#
GTestVersion = '1.6.0'
GTestStem = 'gtest-' + GTestVersion
GTestBuildIncDir = [GTestStem,
                    GTestStem + '/include',
                    ]

GTestIncDir = GTestStem + '/include/gtest'
GTestLibDir = 'lib'
GTestLibFlags = ['gtest', 'gtest_main', 'pthread']

## Configuration information for Armadillo matrix library
#

ArmadilloLibFlags = ['armadillo'];

## Locations of libraries installed on system in standard locations
#
StdIncDir = '/usr/include'
StdLibDir = '/usr/lib'

## Configuration information for COIN libraries
#
CoinUtilsVersion = '2.6.4'
ClpVersion = '1.12.0'
OsiVersion = '0.103.0'
CbcVersion = '2.5.0'

## Standard directory locations of COIN libraries, with slashes added for
#  for convenience.
#
CoinLibLocation = '/usr/local/COIN/'
StdCoinIncDir = '/include/coin'
StdCoinLibDir = '/lib'

CoinUtilsStem = 'CoinUtils-' + CoinUtilsVersion
ClpStem = 'Clp-' + ClpVersion
OsiStem = 'Osi-' + OsiVersion
CbcStem = 'Cbc-' + CbcVersion

if debug:
    CoinUtilsStem += debugString
    CbcStem += debugString
    ClpStem += debugString
    OsiStem += debugString

## Build up include directory names for COIN projects from constituent parts.
#
CoinUtilsIncDir = CoinLibLocation + CoinUtilsStem + StdCoinIncDir
ClpIncDir = CoinLibLocation + ClpStem + StdCoinIncDir
OsiIncDir = CoinLibLocation + OsiStem + StdCoinIncDir
CbcIncDir = CoinLibLocation + CbcStem + StdCoinIncDir

## Build up library names from COIN projects from constituent parts
#
CoinUtilsLibDir = CoinLibLocation + CoinUtilsStem + StdCoinLibDir
ClpLibDir = CoinLibLocation + ClpStem + StdCoinLibDir
OsiLibDir = CoinLibLocation + OsiStem + StdCoinLibDir
CbcLibDir = CoinLibLocation + CbcStem + StdCoinLibDir

## CPLEX
#
CpxStem = '/opt/ibm/ILOG/CPLEX_Studio_Academic123/cplex/'
CpxIncDir = CpxStem + 'include/ilcplex'
CpxLibDir = CpxStem + 'lib/x86-64_sles10_4.1/static_pic'

## Gurobi
#
GrbStem = '/opt/gurobi460/linux64/'
GrbIncDir = GrbStem + 'include'
GrbLibDir = GrbStem + 'lib'

OsiLibFlags = ['Osi', 'CoinUtils']
ClpLibFlags = ['Clp', 'OsiClp']
CbcLibFlags = ['Cbc', 'Cgl']
OsiCpxLibFlags = ['OsiCpx']
OsiGrbLibFlags = ['OsiGrb']
CpxLibFlags = ['cplex', 'ilocplex', 'pthread', 'm']
GrbLibFlags = ['gurobi_c++', 'gurobi46', 'pthread', 'm']

milpIncDirs = [CoinUtilsIncDir,
               ClpIncDir,
               OsiIncDir,
               CbcIncDir,
               CpxIncDir,
               GrbIncDir,
            GTestIncDir,
               ]
milpLibDirs = [CoinUtilsLibDir,
               ClpLibDir,
               OsiLibDir,
               CbcLibDir,
               CpxLibDir,
               GrbLibDir,
               GTestLibDir,
            ]
milpLibFlags = [OsiCpxLibFlags,
                OsiGrbLibFlags,
                CbcLibFlags,
                ClpLibFlags,
                OsiLibFlags,
                CpxLibFlags,
                GrbLibFlags,
                GTestLibFlags,
                ]

## Configuration information for Chemkin source directories and files
#
ChemkinSourceDir = '/mnt/hgfs/DataFromOldLaptop/Data/ModelReductionResearch/Papers/AdaptiveChemistryPaper/AdaptiveChemistry/NonOpenSource/ChemkinII/';
ChemkinSourceList = ['cklib.f', 'pcmach.f','tranlib.f']
ChemkinSourceList = [ChemkinSourceDir + FileName
                     for FileName in ChemkinSourceList]

## Configuration information for Cantera
#
CanteraStem = '/usr/local/cantera'

if debug:
    CanteraStem += debugString

CanteraIncDir = CanteraStem + '/include/cantera'
CanteraLibDir = CanteraStem + '/lib'
CanteraTestingFlags = ['kinetics', 'thermo', 'tpx', 'ctbase', 'm',]
CanteraLibFlags = ['user', 'oneD', 'zeroD', 'equil', 'kinetics', 'transport',
                    'thermo', 'ctnumerics', 'ctmath', 'tpx', 'ctspectra',
                    'converters', 'ctbase', 'cvode', 'ctlapack', 'ctblas',
                    'ctf2c', 'ctcxx', 'ctf2c', 'm', 'm', 'stdc++']

CxxFortranFlags = ['g2c', 'gfortran'];

chemSolverIncDir = [CanteraIncDir,
                    StdIncDir,
                    '/usr/local/include',
                    GTestIncDir,
                    ]
chemSolverLibDir = [StdLibDir,
                    CanteraLibDir,
                    GTestLibDir,
                    ]
chemSolverLibFlags = [GTestLibFlags,
                      CxxFortranFlags,
                      CanteraLibFlags,
                      ArmadilloLibFlags,
                      ]
canteraGTestLibFlags = CanteraTestingFlags + GTestLibFlags

#canteraGTestLibFlags = ['kinetics', 'thermo', 'tpx',
#                        'ctbase',  'm', 'gtest', 'gtest_main', 'pthread']

The main SConstruct file calls a bunch of SConscript files to build modules containing the major features I have in my code. Moving most of the build commands to SConscript files makes the SConstruct file really simple:

## \file SConstruct
#  \brief Compiles the library and compiles tests.
#

import SCons
from build_config import *

## \brief Build up directory names of each COIN library from package names
#         and versions.
#

## Overall SCons environment
#
env = Environment();

## Compile Google Test from source using SConscript file.
#
GTestAllLib, GTestMainLib = env.SConscript('gtest.scons',
                                           exports=['env'])

## Compile MILP solver module and tests from source using SConscript file.
#
milpSolverTest = env.SConscript('milpSolver.scons',
                                exports=['env'])

## Compile chemistry solver module and associated tests from source
#  using SConscript file.
chemSolverTest, canteraGTest = env.SConscript('chemSolver.scons',
                                              exports=['env'])

## Since all tests use GTest, make the dependency of the module
# tests on the GTest libraries explicit.
env.Depends(milpSolverTest, [GTestAllLib, GTestMainLib])
env.Depends(chemSolverTest, [GTestAllLib, GTestMainLib])
env.Depends(canteraGTest, [GTestAllLib, GTestMainLib])

#env.AddPostAction(milpSolverTest, milpSolverTest[0].abspath)
testAlias = env.Alias('test', [milpSolverTest, chemSolverTest])
AlwaysBuild(testAlias)

Then all of three SConscript files have the extension .scons and break up the project into modules representing different functionality.

Google Test SConscript file:

## \file gtest.scons
#  \brief SConscript file that contains information for SCons build of
#  GTest. Use Python syntax highlighting for source.

from build_config import *
Import('env')

## Compile Google Test from scratch.
#
GTestAllLib = env.Library('lib/libgtest.a', 'gtest-1.6.0/src/gtest-all.cc',
                      CPPPATH = GTestBuildIncDir,
                      CXXFLAGS = flags)
GTestMainLib = env.Library('lib/libgtest_main.a',
                           'gtest-1.6.0/src/gtest_main.cc',
                           CPPPATH = GTestBuildIncDir,
                           CXXFLAGS = flags)

Return('GTestAllLib', 'GTestMainLib')

Mixed-integer linear programming solver SConscript file:

## \file milpSolver.scons
#  \brief SConscript file that contains information for SCons build of
#  mixed-integer linear programming solver module. Use Python syntax
#  highlighting for source.

from build_config import *
Import('env')

## Compile MILP solver module and tests.
#

##milpSolver = env.Object('milpSolver.cpp',
            ##                         CPPPATH = milpIncDirs,
##                         LIBPATH = milpLibDirs,
##                         CXXFLAGS = flags)
milpSolverTest = env.Program('milpSolverUnitTest',
                                  ['milpSolverTest.cpp',
                                   'milpSolver.cpp'],
                                  CPPPATH = milpIncDirs,
                                  LIBPATH = milpLibDirs,
                                  LIBS = milpLibFlags,
                                  CXXFLAGS = flags,
                                  LINKFLAGS = ['-Wl,-rpath,' + OsiLibDir])

Return('milpSolverTest')

Chemistry engine SConscript file:

## \file chemSolver.scons
#  \brief  SConscript file that sets up SCons build of chemistry solver module.
#  Use Python syntax highlighting for source.

from build_config import *
Import('env')

## Compile CHEMKIN interpreter.
#
ckInterp = env.Program('ckinterp', ChemkinSourceDir + 'ckinterp.f')

## Enforce explicit dependence of CHEMKIN library on CHEMKIN
#  parameter file 'ckstrt.f' because SCons' scanner won't pick it up.
env.Depends('cklib.f','ckstrt.f')

chemSolverTest = env.Program('chemSolverUnitTest',
                        ['chemSolverTest.cpp',
                         'chemSolver.cpp',
                         'ckwrapper.f90'] + ChemkinSourceList,
                        CPPPATH = chemSolverIncDir,
                        LIBPATH = chemSolverLibDir,
                        LIBS = chemSolverLibFlags,
                        CXXFLAGS = flags,
                        FORTRANFLAGS = flags,
                        F90FLAGS = flags)

canteraGTest = env.Program('canteraGTest',
                           'canteraGTest.cpp',
                           CPPPATH = chemSolverIncDir,
                           LIBPATH = chemSolverLibDir,
                           LIBS = canteraGTestLibFlags,
                           CXXFLAGS = flags)

canteraMemTestLibFlags = CanteraTestingFlags

canteraMemTest = env.Program('canteraMemTest',
                             'canteraMemTest.cpp',
                             CPPPATH = chemSolverIncDir,
                             LIBPATH = chemSolverLibDir,
                             LIBS = canteraMemTestLibFlags,
                             CXXFLAGS = flags)

Return('chemSolverTest', 'canteraGTest')

The combination of these five files is probably a bit longer than the original long file, but it's easier for me to manage because I can separate the build into a configuration file, and a bunch of mostly uncoupled units, so I don't have to keep track of the whole build in my mind at one time.


问:
Is code ownership a code smell?
This is something I've been thinking about ever since I read this answer in the controversial programming opinions thread:

Your job is to put yourself out of work.

When you're writing software for your employer, any software that you create is to be written in such a way that it can be picked up by any developer and understood with a minimal amount of effort. It is well designed, clearly and consistently written, formatted cleanly, documented where it needs to be, builds daily as expected, checked into the repository, and appropriately versioned.

If you get hit by a bus, laid off, fired, or walk off the job, your employer should be able to replace you on a moment's notice, and the next guy could step into your role, pick up your code and be up and running within a week tops. If he or she can't do that, then you've failed miserably.

Interestingly, I've found that having that goal has made me more valuable to my employers. The more I strive to be disposable, the more valuable I become to them.

And it has been discussed a bit in other questions, such as this one, but I wanted to bring it up again to discuss from a more point blank "it's a code smell!!" point of view - which hasn't really been covered in depth yet.

I've been a professional developer for ten years. I've had one job where the code was well written enough to be picked up relatively quickly by any decent new developer, but in most cases in industry, it seems that a very high level of ownership (both individual and team ownership) is the norm. Most code bases seem to lack the documentation, process, and "openness" that would allow a new developer to pick them up and get working with them quickly. There always seem to be lots of unwritten little tricks and hacks that only someone who knows the code base very well ("owns" it) would know about.

Of course, the obvious problem with this is: what if the person quits or "gets hit by a bus"? Or on a team level: what if the whole team gets food poisoning when they go out on their team lunch, and they all die? Would you be able to replace the team with a fresh set of new random developers relatively painlessly? - In several of my past jobs, I can't imagine that happening at all. The systems were so full of tricks and hacks that you "just have to know", that any new team you hire would take far longer than the profitable business cycle (eg, new stable releases) to get things going again. In short, I wouldn't be surprised if the product would have to be abandoned.

Obviously, losing an entire team at once would be very rare. But I think there is a more subtle and sinister thing in all this - which is the point which got me thinking to start this thread, as I haven't seen it discussed in these terms before. Basically: I think a high need for code ownership is very often an indicator of technical debt. If there is a lack of process, communication, good design, lots of little tricks and hacks in the system that you "just have to know", etc - it usually means that the system is getting into progressively deeper and deeper technical debt.

But the thing is - code ownership is often presented as a kind of "loyalty" to a project and company, as a positive form of "taking responsibility" for your work - so it's unpopular to outright condemn it. But at the same time, the technical debt side of the equation often means that the code base is getting progressively less open, and more difficult to work with. And especially as people move on and new developers have to take their place, the technical debt (ie maintenance) cost starts to soar.

So in a sense, I actually think that it would be a good thing for our profession if a high level of need for code ownership were openly seen as a job smell (in the popular programmer imagination). Instead of it being seen as "taking responsibility and pride" in the work, it should be seen more as "entrenching oneself and creating artificial job security via technical debt".

And I think the test (thought experiment) should basically be: what if the person (or indeed, the whole team) were to vanish off the face of the Earth tomorrow. Would this be a gigantic - possibly fatal - injury to the project, or would we be able to bring in new people, get them to read the doccos and help files and play around with the code for a few days - and then be back in business in a few weeks (and back to full productivity in a month or so)?

答:
Tackling your question from a slightly different point of view, code ownership is NOT a code smell. It is instead a management and resourcing smell.

Think about what a code smell means.  It's a metaphor that tells you when you look at your code, that something isn't quite right with the code and/or the design of the software, but the problem may not be so obvious as to be able to put your finger on what the exact problem may be but you've probably got a good idea regardless. In your home, if something smells bad, you follow your nose until you locate the source of the problem, and then you do something about it. In the same way, if the code has a problem you investigate and change it until it becomes less of a problem, or as some might say, beautiful or well-crafted.

Code ownership on the other hand can be a serious problem. It suggests a lack of oversight, and a risk that if the code owner leaves, that knowledge about the code and the specific problems that it solves will no longer be available to the company.  This has nothing to do with the actual code itself. It basically comes down to a risk management situation in the same way that leads us to keep backups of our data, and applies just as equally to task ownership, or document ownership in other areas of the company.

I think that it's fine to describe other business problems as smells, but certainly not to imply that just because there is a smell, that it has specifically to do with code.

ContentLicense: CC BY-SA 3.0

Title: Is code ownership a code smell?


问:
Source/Version control for application used by multiple companies
Do you find it easier to keep a branch for each company that uses software you support/develop? Each company will want "customizations" and I am just trying to figure out the best way to handle those changes. A branch for each company makes sense in the short term but I could see how this could cause issues with upgrades when a new version is developed.

Is better to have a branch for each company or to setup customizations so that they are always merged to a main branch to minimize issues with future releases?

答:
Supporting a product that is customized for multiple customers is not a version control problem.  It is a design and distribution problem.

Maintaining multiple code bases is a real pain.  It's bad enough to need to duplicate the code base to support multiple platforms and languages, let alone multiple customers. Better instead to build a modular product.  Sure, there will be elements that will require a larger degree of customization, and a small percentage of overlap where the code may need to be effectively duplicated, but it is better to create a very finely grained modular system design to cope, instead of attempting to branch where the risk is that the branches may become so divergent as to never be able to merge.

I myself work on an API that presently supports 3 hardware platforms, and greater than 3 customers.  At the minimum, we would need to manage 9 separate code-bases if our code was not designed to be modular from the start.  When I first arrived at the company nearly 10 years ago, the existing code base used conditional compiler directives to deal with 3 variants on a single platform, was about 50 times smaller than the present API incarnation, and took twice the effort to maintain. It took a lot of work, but we now have a product that never needs to be branched, and will be able to support literally any number of clients, platforms, and customizations.

So to specifically answer your question, It is better to maintain a single main branch provided you implement a design that will support a more modular development approach, and if your customization is required to be unavailable to more than a single customer, then you solve the problem in the way that your modules are accessed by your code, and in how they are deployed.


问:
Is it a code smell if an object knows a lot of its owner?
In our Delphi 2007 application we are using a lot of the following constructs

```<code>FdmBasic:=TdmBasicData(FindOwnerClass(AOwner,TdmBasicData));
</code>```

The FindOwnerClass travels the Owner hierarchy of the current component upwards to find a specific class (in the example TdmBasicData). The resulting object is stored in the Field variable FdmBasic. We use this primarily to pass datamodules along.

Example:
When generating a report, the resulting data is compressed and stored in a Blob field of a table accessed through a datamodule TdmReportBaseData. In a separate module of our application, there is functionality to show the data from the report in a Paged form using ReportBuilder. The main code of this module (TdmRBReport), uses a class TRBTempdatabase to convert the compressed blob data into different tables that are usable in the Reportbuilder runtime reportdesigner.
TdmRBReport has access to TdmReportBaseData for all kinds of report-related data (type of report, report calculationsettings, etc). TRBTempDatabase is constructed in TdmRBReport but has to have access to TdmReportBasedata. So this is now done using the construction above:

```<code>constructor TRBTempDatabase.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);

  FdmReportBaseData := TdmRBReport(FindOwnerClass(Owner, TdmRBReport)).dmReportBaseData;
end;{- .Create }
</code>```

My feeling is that this means that TRBTempDatabase knows a lot of its owner, and I was wondering if this is some sort of code smell or Anti-pattern.

What are your thoughts about this? Is this a code smell? If so, what is a better way?

答:
One of the difficulties with having child objects knowing too much about the parent is that you end up implementing patterns that can (and most often do) become too tightly coupled, which creates major dependency headaches and often later becomes very difficult to modify & maintain safely later on.

Depending on how deeply your two classes are connected, it sounds a bit like Fowler's description of the Feature Envy or Inappropriate Intimacy code smells are evident.

There appears to be need to load or read a class with data, in which case you could use a number of alternate patterns to break the dependency between the child and it's chain of parents, and it sounds like you need to delegate the task of accessing your data class rather than making the data accessor class responsible for doing everything itself.


问:
Is "Code Smell" still a useful metaphor, or has misuse of the term subverted its meaning?
I've come across some
comments
and
answers
on Programmers.SE that decry the use of the phrase "Code Smell" and I've been wondering what the reasoning is for those who dislike it.  I first encountered this term when I read Fowler's
Refactoring
back in 2000, yet it's only in the last year or two that I've heard any
complaint
about
the use of the phrase.

Q: Would it be correct to say that the phrase Code Smell has recently started to fall into disfavor, and if so why does/should this metaphor no longer apply?

I am looking for well defined answers supported by sound reasoning as I realize that this question is borderline subjective.

答:
The term suffers from what all conceptual mixed metaphors suffer from: Ambiguity. What exactly is considered code smell? Jeff Atwood assembled an extensive list of code smells, but if you go through it, do you feel confident in saying that we (or any other two developers) would apply the term to the same conditions?

It's just a silly little metaphor that, as all silly little metaphors, was never intended to be taken seriously. When Kent Beck coined the term, this is what he had in mind:

A code smell is a surface indication that usually corresponds to a deeper problem in the system.

Code smell is a hint that something may be wrong, a red flag that you should investigate further, and not a generic label for bad code. It's an extremely accessible metaphor, translatable to most languages (I think?), relatively easy to explain to beginners, coined by Kent Beck and popularized by Martin Fowler (read: hype). Hence, prone to abuse.

Most mentions of the term on Programmers.SE are replaceable by a more specific term. Not necessarily a more formal term, just a more specific one, that would guide answers a bit better. There are even some "code smell" questions that aren't actually about something awry in the code itself.

In the context of Programmers.SE questions and answers, it would be preferable to avoid metaphors, idioms, analogies, and other literary devices. Every ambiguity is a potential comment war, and literary devices are prone to be interpreted differently per level of expertise, and don't always translate to the world wide audience of the site. As Bjarne Stroustrup put it best:

Proof by analogy is fraud.

All that said, I really like the term and would never give grief to anyone for using it.

Is any of this on topic?


问:
What is the ideal length of a method for you?
In object-oriented programming, there is of course no exact rule on the maximum length of a method , but I still found these two quotes somewhat contradicting each other, so I would like to hear what you think.

In Clean Code: A Handbook of Agile Software Craftsmanship, Robert Martin says:

The first rule of functions is that they should be small. The second
  rule of functions is that they should be smaller than that. Functions
  should not be 100 lines long. Functions should hardly ever be 20 lines
  long.

and he gives an example from Java code he sees from Kent Beck:

Every function in his program was just two, or three, or four lines
  long. Each was transparently obvious. Each told a story. And each led
  you to the next in a compelling order. That’s how short your functions
  should be!

This sounds great, but on the other hand, in Code Complete, Steve McConnell says something very different:

The routine should be allowed to grow organically up to 100-200 lines,
  decades of evidence say that routines of such length no more error
  prone then shorter routines.

And he gives a reference to a study that says routines 65 lines or long are cheaper to develop.

So while there are diverging opinions about the matter, is there a functional best-practice for you?

答:
While I agree with other's comments when they said there's no hard rule about right LOC number, I bet if we look back at the projects we've looked at in the past and identify every function above, let's say 150 lines of code, I'm guessing we would come to a consensus that 9 out of 10 of those functions break SRP (and very likely OCP as well), have too many local variables, too much control flow and are generally hard to read and maintain.

So while LOC may not be a direct indicator of bad code, it is certainly a decent indirect indicator that certain function could be written better.

On my team I fell into the position of a lead and for whatever reason, people seem to be listening to me. What I generally settled on is to tell the team that while there's no absolute limit, any function more 50 lines of code should at a minimum raise a red flag during code review, so that we take a second look at it and re-evaluate it for complexity and SRP/OCP violations. After that second look, we might leave it alone or we might change it, but at least it makes people think about these things.

OwnerDisplayName: iPhoneDeveloper


答:
It depends, seriously, there really isn't a solid answer to this question because the language you are work with matters, the five to fifteenth lines mentioned in this answer might work for C# or Java, but in other languages it doesn't give you much to work with. Likewise, depending upon the domain you are working in, you might find yourself writing code setting values in a large data structure. With some data structures you might have tens of elements that you need to set, should you break things out in to separate functions just because your function is running long?

As others have noted, the best rule of thumb is that a function should be a single logical entity that handles a single task. If you try to enforce draconian rules that say that functions can't be longer than n lines and you make that value too small your code will grow harder to read as developers try and use fancy tricks to get around the rule. Likewise, if you set it too high it will be a non-issue and can lead to bad code though laziness. Your best bet is to just conduct code reviews to ensure that functions are handling a single task and leave it at that.

Title: What is the ideal length of a method for you?

Body:
In object-oriented programming, there is of course no exact rule on the maximum length of a method , but I still found these two quotes somewhat contradicting each other, so I would like to hear what you think.

In Clean Code: A Handbook of Agile Software Craftsmanship, Robert Martin says:

The first rule of functions is that they should be small. The second
  rule of functions is that they should be smaller than that. Functions
  should not be 100 lines long. Functions should hardly ever be 20 lines
  long.

and he gives an example from Java code he sees from Kent Beck:

Every function in his program was just two, or three, or four lines
  long. Each was transparently obvious. Each told a story. And each led
  you to the next in a compelling order. That’s how short your functions
  should be!

This sounds great, but on the other hand, in Code Complete, Steve McConnell says something very different:

The routine should be allowed to grow organically up to 100-200 lines,
  decades of evidence say that routines of such length no more error
  prone then shorter routines.

And he gives a reference to a study that says routines 65 lines or long are cheaper to develop.

So while there are diverging opinions about the matter, is there a functional best-practice for you?


答:
Contrary to previous answers, authors of which live in ideal world, I have to say:
Branches (with good VCS) is single way (not "easier": just because there are not harder way) to maintain customer's customisation.

Permanent merges from vanilla-branch to customs isn't easy time, but - it works

Title: Source/Version control for application used by multiple companies

Body:
Do you find it easier to keep a branch for each company that uses software you support/develop? Each company will want "customizations" and I am just trying to figure out the best way to handle those changes. A branch for each company makes sense in the short term but I could see how this could cause issues with upgrades when a new version is developed.

Is better to have a branch for each company or to setup customizations so that they are always merged to a main branch to minimize issues with future releases?


问:
What's the simplest example out there to explain the difference between Parse Trees and Abstract Syntax Trees?
To my understanding, a parser creates a parse tree, and then discards it thereafter. However, it can also pop out an abstract syntax tree, which the compiler supposedly makes use of.

I'm under the impression that both the parse tree and the abstract syntax tree are created under the parsing stage. Then could someone explain why these are different?

答:
A parse tree is also known as a concrete syntax tree.

Basically, the abstract tree has less information than the concrete tree. The concrete tree contains each element in the language, whereas the abstract tree has thrown away the uninteresting pieces.

For example the expression: (2 + 5) * 8

The concrete looks like this

```<code>  ( 2  + 5 )  * 8
  |  \ | / |  | |
  |   \|/  |  | |
   \___|__/   | |
       \______|/
</code>```

Whereas the abstract tree has:

```<code>2  5
 \/
  +  8
   \/
   *
</code>```

In the concrete cases the parentheses and all pieces of the language have been incorporated into the tree. In the abstract case the parentheses are gone, because their information has been incorporated into the tree structure.

Body:
A parse tree is also known as a concrete syntax tree.

Basically, the abstract tree has less information than the concrete tree. The concrete tree contains each element in the language, whereas the abstract tree has thrown away the uninteresting pieces.

For example the expression: (2 + 5) * 8

The concrete looks like this

  ( 2  + 5 )  * 8
  |  \ | / |  | |
  |   \|/  |  | |
   \___|__/   | |
       \______|/

Whereas the abstract tree has:

2  5
 \/
  +  8
   \/
   *

In the concrete cases the parentheses and all pieces of the language have been incorporated into the tree. In the abstract case the parentheses are gone, because their information has been incorporated into the tree structure.


问:
Can web technologies, like PHP, HTML, and CSS, be used for desktop development?
I have to develop an application for a gas agency where a database will store consumer numbers, names, addresses, and date delivered for a town with nearly 20,000 people. I only know PHP, MySQL, CSS, jQuery, and other web based technologies. Is it possible to deliver a package to the client using these technologies that can be installed on a computer and used locally?

This application will run on a single PC for a single user. I'm familiar with tools such as XAMPP, but I do not want to run a web server locally. Both the application and database will be securely running on a particular computer. In the future, if this application must be on multiple computers, I want the database and application to be portable.

I have searched for similar questions on Stack Overflow and I did find some recommendations like Jetty, Adobe Air and the recent Web SQL database. Is PHP GTK also a viable tool for building professional applications?

I would like a recommendation specific for this kind of app.

答:
Why screw around trying to hack something together with technologies that werent designed for desktop app development, when you can use an actual desktop development tool to create an application with far less hassle?

Whatever you think you'd be saving by not learning a new language, you'd be wasting by trying to fit a square peg into a round hole.  Use the right tool for the job.

Download something like Visual Studio Express for either C# or VB.Net, and try that out.  Or Delphi, if you have a little budget to work with.  You say you know PHP & MySQL.  Well, SQL is SQL, so thats not an issue regardless of the language you choose.  C# syntax shouldnt be too different from what you are used to looking at in PHP.  But VB.Net shouldnt be too hard to approach, either.  Delphi makes nice self-contained executables, making distribution much easier.  Those are just a few options.  There's also C++. Python, Java....


问:
Why should identifiers not begin with a number?
Most programming languages appear to be designed to not allow one to declare an identifier that starts with a number. I was just curious to know the reason. I have already searched the web, but couldn't find a satisfactory explanation.</p>

答:
It's not a logically required rule for programming language but just the convention used by  many language designers.

I can design radically different language that allow all character for identifiers.
For all code lines, first 20 character will describe the statement type then next 20 character will define the first symbol for the statement, and next 20 character is operand for the statement. This language will be executed on a stack processor.

```<code>01234567890123456789 01234567890123456789 01234567890123456789

decl symbol          12345
assign value         12345                12345
decl symbol          99999
assign value         99999                12345
push                 12345
push                 99999
add
print top
</code>```

This code could be translated in C as below:

```<code>int i12345 = 12345;
int i99999 = 12345;
printf("%d", i12345+i9999);
</code>```

That's all. It's meaningless and the no-number-in-identifiers rule is also pointless in logical ground.

Body:
It's not a logically required rule for programming language but just the convention used by  many language designers.

I can design radically different language that allow all character for identifiers.
For all code lines, first 20 character will describe the statement type then next 20 character will define the first symbol for the statement, and next 20 character is operand for the statement. This language will be executed on a stack processor.

01234567890123456789 01234567890123456789 01234567890123456789

decl symbol          12345
assign value         12345                12345
decl symbol          99999
assign value         99999                12345
push                 12345
push                 99999
add
print top

This code could be translated in C as below:

int i12345 = 12345;
int i99999 = 12345;
printf("%d", i12345+i9999);

That's all. It's meaningless and the no-number-in-identifiers rule is also pointless in logical ground.


问:
When stuck, how quickly should one resort to Stack Overflow?
I'm self-learning iOS development through the iTunes U CS193p course, and I often find myself stuck. I've been trying to get unstuck myself, but it might take me hours and hours to figure out what I'm doing wrong, be it missing a method or not really getting a whole concept like delegation.

I'm worried that I might be wasting too much time, and I'd be better off going to Stack Overflow shortly after I get stuck so I can move on.

In your experience, does quickly asking on Stack Overflow hamper the learning process or improve it?

答:
When I am working with new developers, I encourage them to come ask questions after five or ten minutes where they are not making progress.

That has two benefits: the first is that they can get help without too much time spent staring at a problem, but they only ask when they are not getting somewhere.  If they are learning - even on something that isn't ultimately the answer - they are much more likely to usefully retain that information.

The second is that after about that much time they have to explain the problem to someone else.  That solves a huge proportion of problems, because going through it end-to-end in order means you can spot the thing that you missed in your earlier work.

Since it sounds like you are doing this alone, try turning to a stuffed toy, or the clock, or the wall, and asking that about the problem.  Explain it as you would to a person, and see if that fixes things.

If it doesn't, and you are not making progress, ask someone.  Spending more than five or ten minutes stuck is a waste of your time - unless you go on to do something else, then come back to the problem with a fresh mind.


答:
I had a old woodworking teacher as a kid who wanted me to remember to make sure that I didn't leave a mess with varnish on my completed projects.  He gave me a silly sounding name to remind me to clean up the varnish before it dried, saying that if he called it something nice I'd forget it, but a silly name I'd remember for ever. He was right, and to this day I associate the word "snotters" with drips of varnish left to dry and make a woodworking project look ugly.

There is power in naming things. We know this in how we sometimes agonize over how we should name our classes and variables in code, and how getting names right helps to define the content of our methods. Likewise I am sure that this is the real power in using an analogy to describe a concept as ephemeral as the potential for a design problem in working code. Agile developers like to get it to work, and then refactor to get it to work better, however a sensible approach needs to be employed to ensure you are refactoring for the right reasons.  As Yannis has correctly stated (and I'll paraphrase), that which seems bad to one person may not necessarily be considered bad by another, and thus the potential for a problem does not logically imply the actual existence of a problem.

Perhaps the analogy isn't quite accurate. Maybe a touch, sound, taste, or visual analogy would be more direct. However the problem with applying a non-olfactory analogy is that it is hard to come up with a good short name to describe code that might end up bothering you in some subtle way, while the word smell implies a subtlety that might have otherwise been overlooked had it not been for some factor that brought it momentarily to your attention. What I like about using the word smell is that it offers a way to describe that something may need to be looked at for further tuning, while not necessarily implying that there is anything wrong... like a good and stinky blue cheese might smell awful yet still taste nice. It's subtle - the phrase... perhaps less so the cheese... or this analogy!

Possibly one of the reasons that code smell seems to be treated like bad air on Programmers.SE (yes, pun intended) is that many questions tend to ask "is this a smell", yet without providing enough context. Thus we end up with a pretty useless series of questions that resemble the following:

Q: Could it indicate a problem?
  A: Maybe/Potentially.

  Q: Does it indicate a problem?
  A: How will we know without reading all of your code?

Perhaps the underlying issue with people not liking the term "code smell", or perhaps more precisely the reason why many people misuse the phrase, is simply because their definition is being taken from a purists perspective, rather than pragmatically, or perhaps they simply misunderstand what the analogy of a code smell is really meant to represent. Based on the C2-wiki's Code Smell definition it appears that the meaning of the phrase remains sound, and it's usage continues to be applicable, while my now lamented woodwork teacher's wisdom also remains sound, because like Snotters, the phrase Code Smell is hard to forget and will likely continue to be used - and possibly abused - for a long time to come.

Title: Is "Code Smell" still a useful metaphor, or has misuse of the term subverted its meaning?

Body:
I've come across some
comments
and
answers
on Programmers.SE that decry the use of the phrase "Code Smell" and I've been wondering what the reasoning is for those who dislike it.  I first encountered this term when I read Fowler's
Refactoring
back in 2000, yet it's only in the last year or two that I've heard any
complaint
about
the use of the phrase.

Q: Would it be correct to say that the phrase Code Smell has recently started to fall into disfavor, and if so why does/should this metaphor no longer apply?

I am looking for well defined answers supported by sound reasoning as I realize that this question is borderline subjective.


答:
Stack Overflow, and every other Stack Exchange site, has a set of guidelines for asking questions:

Do your homework

Be specific

Make it relevant to others

Be on-topic

Keep an open mind

"Do your homework" implies that you shouldn't be too quick to ask. "Be specific" implies that you understand the domain of your problem well enough to outline it concisely. If not, "do your homework" applies. "Keep an open mind" implies that you should be ready to explore solutions that you never thought would be relevant to your problem.

So it's not about asking on Stack Overflow, but whether your questions are good. Essentially the required effort is a learning experience in itself. If you do it right, you'll probably learn a couple of new things just by searching for duplicates. If not, ping me, I got a couple of down votes to spare. :P

Title: When stuck, how quickly should one resort to Stack Overflow?

Body:
I'm self-learning iOS development through the iTunes U CS193p course, and I often find myself stuck. I've been trying to get unstuck myself, but it might take me hours and hours to figure out what I'm doing wrong, be it missing a method or not really getting a whole concept like delegation.

I'm worried that I might be wasting too much time, and I'd be better off going to Stack Overflow shortly after I get stuck so I can move on.

In your experience, does quickly asking on Stack Overflow hamper the learning process or improve it?


答:
Personally, when I started programming I spent a lot of time trying to figure things out. I could spend hours on trying to fix something. I don't see those hourse as not being productive, in those hours I learned how to look for things online and I really gained insight in how my programming language worked. So I think asking too quickly on SO robs you of a learning experience. Getting into programming just takes a very long amount of time.

Then there is a question of how soon to ask, I'd say spending a few hours on a particular problem is not long (note that I come from an academic background, maybe in commercial backgrounds this is less accepted).

And when you do ask a question on SO, invest time into the question:

When you have an error, post the exact text of the error. Also post the code that lead to the error, preferably a self contained example that runs on any computer.

Show what you have tried already.

Mention versions of the software you use: compiler versions, version of python.

These are just a few points. By investing time into a question you respect the people on SO, you use their time for free. More importantly, you structure you problem. This might already solve your problem :).

Just my 2 ct...


问:
How does one effectively implement the 80/20 rule for developer time?
I am a developer at a bank where an 80/20 rule is implemented. 80% of the time you work on your assigned project and 20% you work on something that interests you. However, I'm not really sure how to do that. I'm hoping there are others out there who can provide me with advice on how to.

My problem is that when I really get 'in the zone', all I can think about is the task at hand. If this is a 20%'er, the last thing I want to do is stop and go, lose momentum and go back to work on my main project.

Am I suppose to work 4 days on something and 1 day on something else? or 4 weeks and 1 week?

I know this depends on the company, but I'm hoping to get a feel for how this is done industry wide.

答: I think if the 20% is company policy, and you get your main projects done, the boss doesn't really care where you spend those 20%. I would try to not splinter up your time too much, at least one day working consecutively on something. And when you get in the zone, just spend a day or two more and compensate later by working more on your main projects.</p>


答: Split all your tasks in sub-tasks (the "80%" tasks as well as the "20% tasks") and write them down. Each tasks should be small enough to be done in a few hours at most, and big enough that you can get into "the zone" for it (if it is a task needing your concentration in that way). Then, choose an order to work on those tasks fulfilling the 80/20 rule per week or per month, whatever will be appropriate in your company.</p>

Title: How does one effectively implement the 80/20 rule for developer time?

Body:
I am a developer at a bank where an 80/20 rule is implemented. 80% of the time you work on your assigned project and 20% you work on something that interests you. However, I'm not really sure how to do that. I'm hoping there are others out there who can provide me with advice on how to.

My problem is that when I really get 'in the zone', all I can think about is the task at hand. If this is a 20%'er, the last thing I want to do is stop and go, lose momentum and go back to work on my main project.

Am I suppose to work 4 days on something and 1 day on something else? or 4 weeks and 1 week?

I know this depends on the company, but I'm hoping to get a feel for how this is done industry wide.


答:
For the most part this has nothing to do with making it easy for the compiler writers and parsing efficiency, but, more to do with designing a syntax that encourages clear readable and unambiguous code.

Its the language designers thought it would be nice to be able to write numeric literals like the number 1 as just plain 1 .

It would be quite possible to design a language syntax where numeric literals were quoted in some way for example tildas so the numberic literal for number one was encoded as ~1~ and anything not a keyword and not enclosed in quotes was treated as a variable name.

So you could code statements like:

```<code>1 = ~2~
two = 1 * ~2~
</code>```

But also:

```<code>2 = ~3~
six = 2 + 2
</code>```

Whatever syntax you choose ambiguous and hard to follow code is unavoidable.

The C language and most of the "curly brackets" languages descended from C also thought it a good idea to allow programmers to code Octal and Hexadecimal literals directly, and, to specify the type of the literal if this was important.  So

```<code>010  // Octal 10 = 8;
0x10 // Hexadecimal 10 = 16;
5l   // long integer with decimal value 5
2.0d // double float with value 2
</code>```

So even if you allowed variable names start with a number followed by a combination of numbers and letter that included at least one letter you would present the programmer with the problem of deciding whether a given group formed a variable name or a numeric literal
so

```<code>2lll = 22 // OK
2ll  = 2  // compiler error
</code>```

Such ambiguity would not help anyone writing or reading a program.

For a closely related real world example you could look at PL/1 language whose designers thought that being able to use keywords as variable names was a good idea so that:

```<code>IF THEN THEN THEN = ELSE; ELSE ELSE = THEN;
IF IF THEN ELSE = IF; ELSE THEN = ELSE;
DO WHILE (WHILE = DO); END = WHILE + DO; END;
</code>```

Is valid code which compiles and executes.

Title: Why should identifiers not begin with a number?

Body: Most programming languages appear to be designed to not allow one to declare an identifier that starts with a number. I was just curious to know the reason. I have already searched the web, but couldn't find a satisfactory explanation.</p>

Body:
For the most part this has nothing to do with making it easy for the compiler writers and parsing efficiency, but, more to do with designing a syntax that encourages clear readable and unambiguous code.

Its the language designers thought it would be nice to be able to write numeric literals like the number 1 as just plain 1 .

It would be quite possible to design a language syntax where numeric literals were quoted in some way for example tildas so the numberic literal for number one was encoded as ~1~ and anything not a keyword and not enclosed in quotes was treated as a variable name.

So you could code statements like:

1 = ~2~
two = 1 * ~2~

But also:

2 = ~3~
six = 2 + 2

Whatever syntax you choose ambiguous and hard to follow code is unavoidable.

The C language and most of the "curly brackets" languages descended from C also thought it a good idea to allow programmers to code Octal and Hexadecimal literals directly, and, to specify the type of the literal if this was important.  So

010  // Octal 10 = 8;
0x10 // Hexadecimal 10 = 16;
5l   // long integer with decimal value 5
2.0d // double float with value 2

So even if you allowed variable names start with a number followed by a combination of numbers and letter that included at least one letter you would present the programmer with the problem of deciding whether a given group formed a variable name or a numeric literal
so

2lll = 22 // OK
2ll  = 2  // compiler error

Such ambiguity would not help anyone writing or reading a program.

For a closely related real world example you could look at PL/1 language whose designers thought that being able to use keywords as variable names was a good idea so that:

IF THEN THEN THEN = ELSE; ELSE ELSE = THEN;
IF IF THEN ELSE = IF; ELSE THEN = ELSE;
DO WHILE (WHILE = DO); END = WHILE + DO; END;

Is valid code which compiles and executes.


问:
Common diagnostics which VisualVM is used for
There's a lot of documentation out there on how to use the 'jvisualvm' profiler, but very little (if any) real-life advice on how to analyze all those charts and dumps. I'm just a little overwhelmed by all the graphs and having trouble making sense of it all.

I know this is sort of a monolithic-type question, but there has to be a way to condense its typical uses into a fairly concise list - finding memory leaks, zombie threads and such.

What specifically are  some of the common analyses for VisualVM?

答:
What specifically are some of the common analyses for VisualVM?

I think these are the same as for any other Java profiler. For example, YourKit profiler demos page currently lists the following "most popular" analyses:

CPU profiling

J2EE profiling

Finding a memory leak

Profiling excessive garbage allocations


问:
Is using goto ever worthwhile?
goto is almost universally discouraged. Is using this statement ever worthwhile?

答:
Yes, the goto can be used to benefit the experience of the developer:
http://adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/

However, just as with any powerful tool (pointers, multiple inheritance, etc.), one has to be disciplined using it. The example provided in the link uses PHP, which restricts the usage of the goto construct to the same function/method and disables the ability to jump into a new control block (e.g., loop, switch statement, etc.)

Title: Is using goto ever worthwhile?


答:
In electronic design, I've come across things that just don't make sense. When you look at a circuit and think: "That's silly. There is no sensible reason... it looks like they just plucked something from the air and tried it and a sample of 1 actually by a miracle worked".

That's a design smell. It just pongs of something bad and not quite right. (And gets confirmed later by a complete lack of documentation, and also analysis showing dodginess.)

This kind of thing happens in many fields, and the idea of "smell" as a general feeling of something being not right and needing further digging is a perfectly good one.

The does seem to have been co-opted into a more general expression of a difference of point of view, along the lines of "I don't like your object hierarchy, I think it sucks, so you have code smell". This might be the reason you sense a possible falling into disfavour: subjective difference of opinion vs intuitive feel of not quite right... not the same things at all.


答:
The most of the discouragement comes form a sort of "religion" that has been created aroud God Djikstra that was compelling in the early '60s about it's indiscriminate power to:

jump anywhere into whatever block of code

function not executed from the begining

loops not executed from the beginning

skipped variable initialization

jump away from whatever block of code without any possible cleanup.

This has nothing more to do with the goto statement of the modern languages, whose existence is merely due to support the creation of code structures other than the language provided ones.

In particular the first main point above is anymore permitted and the second is cleaned (if you goto out of a block the stack is unwinded properly and all proper destructors called)

You can refer this answer to have an idea of how even code not using goto can be unreadable.
The problem is not goto itself, but the bad use of it.

I can write an entire program without using if, just for.
Of course, it will not be well readable, an look clumsy and unnecessarily complicated.

But the problem is not for. It's me.

Things like break, continue, throw, bool needed=true; while(needed) {...}, etc. are noting more than masquerade goto to escape away from the scimitars of the Djikstrarian zealots, that -50 years after the invention of modern laguages- still want their prisoners. They forgot what Djikstra was talking about, they only remember the title of his note (GOTO considered harmful, and it was not even his onw title: it was changed by the editor) and blame and bash, bash and blame every contruct having those 4 letter placed in sequence.

It's 2011: it's time to understand that goto has noting to deal with the GOTO statement Djikstra was compelling about.

Title: Is using goto ever worthwhile?

Body: goto is almost universally discouraged. Is using this statement ever worthwhile?


问:
How should I choose the right documentation for my team?
I've asked this question a few days ago about how much documentation should be made and now I realize I may have been asking only half of the question.

My goal is to promote some sort of documentation in order to increase continuous knowledge inside the team.

My team is working mainly on server-side services which extends proprietary devices which are distributed locally (national not global)

In addition we supply APIs and administration web apps to those services.

What would be the best types of documentation for my team?

Should we focus on DFDs, sequence diagrams, class diagrams, just plain text... ?

Are some documentation methodologies better for specific industries or are there some  which are just a waste of time?

I fear this last question would be put down as too subjective, but I do hope there are some standard answers for this question, similarly to when someone asks about good OOP techniques and gets directed to design patterns and SOLID principles.

答:
Do not provide printed documentation to your team because project requirements and implementation architecture are changing during the project life:
Developers don't also read the printed documentation
As soon as the code implementation or requirements are changed then the documentation is not anymore accurate.

Having said that I have no silver bullet except to create class diagrams which are synchronized with requirements and code.


答:
Higher-level control flow constructs tend to correspond to concepts in the problem domain.  An if/else is a decision based on some condition.  A loop says to perform some action repeatedly.  Even a break statement says "we were doing this repeatedly, but now we need to stop".

A goto statement, on the other hand, tends to correspond to a concept in the running program, not in the problem domain.  It says to continue execution at a specified point in the program.  Someone reading the code has to infer what that means with respect to the problem domain.

Of course all the higher-level constructs can be defined in terms of gotos and simple conditional branches.  That doesn't mean that they're merely gotos in disguise.  Think of them as restricted gotos -- and it's the restrictions that make them useful.  A break statement is implemented as a jump to the end of the enclosing loop, but it's better thought of as operating on the loop as a whole.

All else being equal, code whose structure reflects that of the problem domain tends to be easier to read and maintain.

There are no cases where a goto statement is absolutely required (there's a theorem to that effect), but there are cases where it can be the least bad solution.  Those cases vary from language to language, depending on what higher-level constructs the language supports.

In C, for example, I believe there are three basic scenarios where a goto is appropriate.

Breaking out of a nested loop.  This would be unnecessary if the language had a labeled break statement.

Bailing out of a stretch of code (typically a function body) in case of an error or other unexpected event.  This would be unnecessary if the language had exceptions.

Implementing an explicit finite state machine.  In this case (and, I think, only in this case) a goto corresponds directly to a concept in the problem domain, transitioning from one state to a specified other state, where the current state is represented by which block of code is currently executing.

On the other hand, an explicit finite state machine can also be implemented with a switch statement inside a loop.  This has the advantage that every state starts at the same place in the code, which can be useful for debugging, for example.

The main use of a goto in a reasonably modern language (one that supports if/else and loops) is to simulate a control flow construct that's missing from the language.

Title: Is using goto ever worthwhile?


问:
What things do I need to become expert ASP.net MVC
I graduated in computer science. I had pretty good knowledge in Java. I have about a year experiences in ASP.net C# Web form. What other things do I need to know if I want become expert in ASP.net MVC which I able to do a complex eCommerce website?</p>

答:
start from having a basic knowledge of M V C. go through exhaustive tutorial series.What actually they are i.e M, V & C.
you should implement which kind of code How and where.
Go through exhaustive tutorials available here. Watch Videos Available for making the concept clear by watching them implemented here

Get the more insights of MVC by watching some more advanced and detailed videos over here.

Download Sample MVC Applications from asp.net MVC site and try to observe the convention which is followed in those application

i will personally advise you to make sample applications along when you complete learning a concept

of course it will take time but learning is something which should be given time.

OwnerDisplayName: Phạm Sỹ Toàn


答:
If you really want to become an expert in ASP.NET MVC, you'll first need to understand exactly what the MVC pattern is, how it's implemented in this case.

Next, I would get your head around stuff like:

The ASP.NET MVC routing engine - in particular, I'd look at how you can extend it!

How the framework passes parameters to your actions and how it decides which values to assign to which parameters and such

How the strong typing of parameters works, especially with reference types - this is quite a cool feature of ASP.NET MVC

How to implement form and model validation - look at the the client-side scripting that the framework generates for form validation and other stuff

Loosely- and strongly-typed views their partial counterparts

What kinds of data types the framework can send and receive, and how to do that

Error/exception handling - the way this is done in ASP.NET MVC is quite different to any other framework I've experienced

How ASP.NET MVC plays with tools like Entity Framework and LINQ-to-SQL

How Javascript (and jQuery\prototype\other) work with the framework and vice versa - in my experience, they work quite nicely together

Once you've really got your head around these things, the rest is pretty much like any other implementation of the MVC pattern.

As a side note, spend some time learning the Razor syntax. It won't be anything particularly new if you already know Java (and presumably C#), but I always find myself forgetting little things with it!

If I've missed something crucial, add it to the list!

Title: What things do I need to become expert ASP.net MVC

Body: I graduated in computer science. I had pretty good knowledge in Java. I have about a year experiences in ASP.net C# Web form. What other things do I need to know if I want become expert in ASP.net MVC which I able to do a complex eCommerce website?</p>


答:
I believe that the important thing here is to really understand what you're stuck on and to try to get to the principle problem.  After you really root down to what the real issue is then using Stack Overflow is one of the first things I do.

But, it isn't asking a question.

It's searching through all the great answers.  Chances are the problem you're having right now has been had by somebody else and they already asked your question.  If you feel compelled to use Stack Overflow remember that it isn't just a site for you to ask a question when you're stumped.  It is also a repository for some really great information on computer programming; use the search function.

If you searched with a couple different terms/keywords and still get nilch, and you've done your homework and have code samples you can show off and you're really sure your question isn't really a duplicate, then by all means ask your question on Stack Overflow.  That's what the site is there for last I checked.


问:
API design involving standard and custom settings
Suppose a class allows some kind of configuration that has some well-known default values but also has the option of defining a custom value. For example, suppose you want to let users configure a person's title. They can choose from a set of standard values (Mr., Mrs., Ms., etc.) but they can also specify a custom (Dr.). What is the most elegant way to put this in an API? Some examples in Java:

```<code>public class Person {
  public enum Title {MR, MRS, MS, CUSTOM};

  setTitle(Title title, String customTitle);
}
</code>```

This one is quite bad because whenever you specify anything else than Title.CUSTOM the second argument is silently ignored.

So maybe:

```<code>public class Person {
  public enum Title { MR, MRS, MS, CUSTOM};

  setTitle(Title title);
  setCustomTitle(String title);
}
</code>```

I don't really like this one either, because it's not immediately clear that calling one will overwrite the effects of the other method. Is there a more elegant interface that I'm overlooking?

答:
You could overload the setTitle mthod

```<code>public class Person{
    public enum Title { MR, MRS, MS, CUSTOM };
    private Title EnumTitle
    private String CustomTitle;

    public void setTitle(Title title); //Does what you'd expect
    public void setTitle(String title)
    {
        EnumTitle = Title.CUSTOM;
        CustomTitle = title;
    }

    /**
     * This does present some problems when returning the value, however.
     *
     * My first thought to solve this problem is to always expect the
     * output of getTitle() to be a string - either the custom title or
     * a string representation of the enum value.
     */
}
</code>```

Body:
You could overload the setTitle mthod

public class Person{
    public enum Title { MR, MRS, MS, CUSTOM };
    private Title EnumTitle
    private String CustomTitle;

    public void setTitle(Title title); //Does what you'd expect
    public void setTitle(String title)
    {
        EnumTitle = Title.CUSTOM;
        CustomTitle = title;
    }

    /**
     * This does present some problems when returning the value, however.
     *
     * My first thought to solve this problem is to always expect the
     * output of getTitle() to be a string - either the custom title or
     * a string representation of the enum value.
     */
}