SlideShare a Scribd company logo
1 of 49
Writing Maintainable Code
by Donald J. Bales
(630) 776-0071
don@donaldbales.com
http://www.pl-sql.org
http://www.donaldbales.com
http://www.facebook.com/donald.j.bales
http://www.linkedin.com/in/donaldbales
Please buy these, I’d need a vacation!
Audience Assessment
 Who has had some experience with object-orientation?
 Who has done some object-oriented programming?
 PL/SQL?
 Who has used CREATE TYPE… AS OBJECT?
 Who has used CREATE TABLE … OF…
 Who knows some Sixties-speak?
“Make it obvious!”
Organization
Stewardship
“Inspect what you expect” (Ziglar)
 Everyone must participate, but you’ll only have a maintainable code base if
someone makes sure you have a maintainable code base.
 Use a source control system – Hint: “The data-dictionary in an Oracle database is
not a source control system!”
The Organizing Principle
 All decisions must evolve around a core principal, in this case, writingAll decisions must evolve around a core principal, in this case, writing
maintainable codemaintainable code. Not two or three principals, just one!
So what is maintain-able code?
 No or low defects – that way there’s less to maintain
 A flexible architecture; expansion capabilities – so when you’re asked to make it
do something additional, you don’t have to start over. “Plan for the future, where
every one always wants more.”
 Modular coding – well defined interfaces mean less internal interaction between
components
 Consistency – so when changes do need to be made, it’s easy. “It's better to be
consistently wrong than it is to be inconsistent.”
 Built-in troubleshooting – when something does go wrong, it’s easy to detect
 Built-in performance profiling – when it’s not expanding well, it’s easy to detect
 Use object-orientation – better taste and less filling
Organizing Your Source Code
 Break scripts into separate files
 Make scripts atomic
 Make scripts re-runnable
 Agree upon and follow naming conventions
(stewardship)
 Name files after the object they will create
“make it obvious”
Example: genders.tab for a table of gender codes
Script Type Suffix
Stand-alone functions .fun
Stand-alone procedures .prc
Type specifications .tps
Type bodies .tpb
Tables (includes column and tables
constraints, sequence, indexes)
.tab
Package specifications .pks
Package bodies .pkb
Views .vw
Inserts .ins
Updates .upd
Deletes .del
Selects .sql
SQL
Naming Conventions
In a Relational Setting…
 Use plural table names, after all, they are collections
 Create a sequence with the same name as its associate table, with a suffix of _ID
– Example: a table named GENDERS would have a corresponding sequence named GENDER_ID.
Why? “Then it’s obvious.”
 Use singular package names
 Create a package for each table’s associated behavior
– Example: a table named GENDERS would have a corresponding package named GENDER. Why?
Atomicity and modularity. If you need to fix something in the GENDER package, only it will be
affected.
Object Type Object Name
Table GENDERS
Sequence GENDER_ID
(table) Package GENDER
Naming Conventions
In an Object-relational Setting…
 Use singular type names
 Create tables for each type’s persistence
– Example: a type named GENDER would have a corresponding table named GENDERS. Why?
“Then it’s obvious.”
 Use plural table names, after all, they are collections
 Create a sequence with the same name as its associate table, with a suffix of _ID
– Example: a table named GENDERS would have a corresponding sequence named GENDER_ID.
Why? “Then it’s obvious.”
Object Type Object Name
Table GENDERS
Sequence GENDER_ID
Type GENDER
Naming Conventions
Consistency
 Use the same name for something at every layer, after all it’s the same thing!
– column -> parameter -> variable, table -> cursor -> record
– attribute -> parameter -> variable, type -> table -> cursor -> record
In other words, none of this %^&*:
birth, birth_date, date_of_birth, born, born_on, bday (ahhh, your making me crazy!)
 Few or no abbreviations – they create ambiguity
Use Constraints
“garbage in, garbage out”
 Use check constraints
– not null
– date = trunc(date) if time is not used needed
– number = trunc(number) for integers, why?
 Use primary keys against an id
 Use unique keys against real-world primary key values
 Use foreign keys – Really. I’m not kidding.
 Use triggers to implement conditional foreign keys
create table TEST_INTEGER (an_integer integer);
insert into TEST_INTEGER values (1.5);
insert into TEST_INTEGER values (1.6);
select * from TEST_INTEGER;
AN_INTEGER
----------
1
2
Plan For Change
 Normalize your types and tables, don't “de-normalize for performance” –
denormalization hinders expansion without modification
 Don't constrain numbers – things are always getting larger and more expensive
 Give varchar2 attributes and columns plenty of room -- things are always getting
larger and being internationalized. You’d be surprised how compact the English
language is when compared to others
 Use CLOBS instead of varchar2(4000) when appropriate – they are now well
supported by all layers of development
 use the AL32UTF8 character set
Plan for Re-Use
 Design with re-use in mind
 Use polymorphic naming – come up with a set of common names before coding;
minimize they set of names used
 Inheritance v. code generation – Inheritance fosters consistency, efficiency, and
modularity
 Encapsulation improves modularity; modularity reduces interdependence; less
interdependence means better maintainability
 Did I say use the AL32UTF8 character set?
Look for the “Gotchas”
 Never use = NULL= NULL in a SQL statement, because NULL is not equal to anything, not
even NULL
 Search you source code for the above mistake
 Never use as UPDATE statement without detectionUPDATE statement without detection, i.e. a WHERE clause that
makes sure you are updating something that has not already been updated
PL/SQL
Prevention and Preparation
 I like to think of maintainability in PL/SQL in two facets:
– Prevention
– Preparation
 First let’s look at prevention
– Naming Conventions
– Anchors
– Explicit Conversion
– Blocking
 And then, preparation
– Comments
– Blocking
– Success and Failure Messages
– Built-in Troubleshooting Capabilities
– Built-in Profiling Capabilities
Naming Conventions
in PL/SQL
 Use data-type prefixes (or suffixes). Why?
 Parameter prefixes:
– ai – argument IN
– aio – argument IN OUT
– ao – argument OUT
 For example:
Data Type Prefix
Argument/Parameter a_
Cursors c_
Dates d_
Numbers n_
Objects o_
Records/Rows r_
Tables/Types t_
Varchar2 v_
Access Modifiers
IN i
IN OUT io
OUT o
PROCEDURE get_code_id_description(
aiov_code in out varchar2,
aon_id out number,
aov_description out varchar2,
aid_on in date ) is
v_code varchar2(30);
v_table_name varchar2(100);
begin
...
end get_code_id_description;
*Anchoring Data Types
in PL/SQL
 *The term anchoring originated with Steven.
 Use %TYPE for attributes and columns
 Use %ROWTYPE for records and rows
 Why? You can use the wrong data type. And, it makes it obvious!
Conversions
in PL/SQL
 Use explicit conversions where you wrap the conversion in a PL/SQL block in order
to detect a failure to convert a character value to a number or date.
 Wrap null-able attributes or columns variables in NVL() with a well known
substitution value so your IF statements don’t fail silently.
Catching Errors
in PL/SQL
 Block (BEGIN … EXCEPTION … END;BEGIN … EXCEPTION … END;) every SQL statement [except a cursor?]
 Block all singletons. Oh yeah. I already stated this above, didn’t I. Must be Important?
 Block all character to number conversions
 Block all character to date conversions
 Never use WHEN OTHERS then NULL;WHEN OTHERS then NULL; (OK. On a rare occasion I do.) Search your
source code for this and make sure it really needs to exist!
 Add PROCEDURE initialize()PROCEDURE initialize() to every package that uses its initialization section, in
order to exercise its initialization section on demand. -- This is the cause of the error
that happens once and then mysteriously goes away!
BEGIN
select description
into v_description
from GENDER;
EXCEPTION
WHEN NO_DATA_FOUND
then v_description := ' ';
WHEN OTHERS then NULL; -- It'll take you hours to find this insect.
END;
create or replace PACKAGE test_initialization_section as
PROCEDURE hello;
PROCEDURE initialize;
end test_initialization_section;
/
create or replace PACKAGE BODY test_initialization_section as
v_greeting varchar2(4);
PROCEDURE hello is
begin
pl(v_greeting);
end hello;
PROCEDURE initialize is
begin
v_greeting := 'Hello';
end initialize;
-- THE INITIALIZATION SECTIONTHE INITIALIZATION SECTION
begin
initializeinitialize;
end test_initialization_section;
/
test_initialization_section.hello code…
test_initialization_section.hello output…
BEGIN test_initialization_section.hello.hello; END;
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "BPS.TEST_INITIALIZATION_SECTION", line 15
ORA-06512: at "BPS.TEST_INITIALIZATION_SECTION", line 20
ORA-06512: at line 1
BEGIN test_initialization_section.hello.hello; END;
PL/SQL procedure successfully completed.
BEGIN test_initialization_section.hello.hello; END;
PL/SQL procedure successfully completed.
SQL> exec test_initialization_section.initialize.initialize;
BEGIN test_initialization_section.initialize; END;
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "BPS.TEST_INITIALIZATION_SECTION", line 15
ORA-06512: at line 1
Comments
in PL/SQL
 Explain mysterious code
 Document every type specification
 Document every package specification
 Document other functions and procedures
 Add PROCEDURE help(); to every type and package specification. Have it extract
you comments from your types and packages by querying SYS.DBA_SOURCESYS.DBA_SOURCE.
 Produce distributable documentation – so somebody else does have to code the
same thing, because they don’t know it already exists!
Name Null? TypeName Null? Type
------------------------------- -------- ----------------------
OWNER VARCHAR2(30)
NAME VARCHAR2(30)
TYPE VARCHAR2(12)
LINE NUMBER
TEXT VARCHAR2(4000)
Error Handling
in PL/SQL
 Block (BEGIN … EXCEPTION … END;BEGIN … EXCEPTION … END;) every SQL [statement except a cursor?]
 Only use an UPDATE statement with detectionUPDATE statement with detection, i.e. with a WHERE clause that
makes sure you are updating something that has not already been updated!
 Block all singletons. Oh yeah. I already stated this above, and again? Important?
 Block all character to number conversions
 Block all character to date conversions
 Every unhandled exception should raise a failure (error) message
 Use well formatted and well know success and failure messages
 I use ORA-20000 – ORA-200## starting from zero at the top of a type or package,
incrementing by one for each new exception clause.
 I use a standard format like this:
when OTHERS then
raise_application_error(-20000, SQLERRM||
' on SELECT GENDERS'||
' in GENDER.get(aiv_code)');
end;
Build-in Trouble-shooting Capability
 Add boolean b_debugboolean b_debug and then use if (b_debug)if (b_debug) for every built-in message
 Add a PROCEDURE set_debug_on()PROCEDURE set_debug_on() and PROCEDURE set_debug_off()PROCEDURE set_debug_off() to your
packages in order to turn debug logging on and off on demand
 Use SYS.DBMS_OUTPUT.put_line()SYS.DBMS_OUTPUT.put_line() to print debug messages to the console
 Or better yet, create a DEBUGDEBUG type or package that logs your if (b_debug)if (b_debug)
messages to a DEBUGSDEBUGS table, so you can view your PL/SQL progam’s progress as it
happens by querying the DEBUGSDEBUGS table.
 Further, create a DEBUGDEBUG package that sets the debug state for each PL/SQL
program in a table that is in turn queried by a given package on start-up
 Dumb down your code by declaring intermediate variablesdeclaring intermediate variables that can be viewed by
a debugger
 I suggest you also learn how to use the debugger!
Build-in Performance Profiling Capabilities
 Use Explain Plan on your SQL statements
 Keep track of the start and stop times of your functions and procedures and log
the output to a profiling table
 Use SYS.DBMS_PROFILER to profile your PL/SQL programs
Test Everything
Write Test Plans
“It doesn't work until you prove it to me!”
 Think about how something should work, and plan on testing it—or better yet,
write the test first!
 On average, 40% of my code is defective the first time I run a well written test unit
against it! And yet, I try so hard to prevent any errors!!!
 Track defects, categorize them, and then update your test plan and then test units
to make sure you test for them in the future
Write Test Units (first?)
“Ah! The proof!”
 Strive for 100% coverage100% coverage, it’s achievable in this development layer
 Write a test package for every type and packagetest package for every type and package
 Name the test package after the package or type it tests, just prefix the name with
TEST_TEST_
 Write one or more test units for each method: function or procedure.
 Name each test unit after the method it tests, just prefix the method name with,
guess what? TEST_TEST_
 Log both success and failure to the console or to a TESTSTESTS table. I personally like a
table because I can more easily do a statistic analysis on my defects.
 And yes, even write test units for the Oracle packages you useyes, even write test units for the Oracle packages you use. You know how
they’re supposed to work because you have some documentation—but do they?
 Write an automated testing package to query the database for all your test units
and run them allrun them all any time you make a change to the source code!
create or replace TYPE base_ as object (
id number,
CONSTRUCTOR FUNCTION base_(
self in out nocopy base_)
return self as result deterministic,
CONSTRUCTOR FUNCTION base_(
self in out nocopy base_,
id number)
return self as result deterministic,
MEMBER FUNCTION sequence_name
return varchar2,
MEMBER FUNCTION table_name
return varchar2,
MEMBER FUNCTION type_name
return varchar2,
MEMBER FUNCTION get_id
return number,
MEMBER PROCEDURE save,
MAP MEMBER FUNCTION to_varchar2
return varchar2,
STATIC PROCEDURE help
)
not final;
/
show errors type base_;
create or replace package body test_base_ as
PROCEDURE test is
begin
test_constructor;
test_constructor_with_id;
test_sequence_name('BASE__ID');
test_table_name('BASE_S');
test_type_name('BASE_');
test_help;
test_to_varchar2('"00000000000000000000000000001234567890"');
end;
PROCEDURE test_constructor is
o_ base_;
begin
pl(chr(10)||'base_: test_constructor');
o_ := new base_;
pl('base_: test_constructor passed.');
exception
when OTHERS then
pl('base_: test_constructor ***FAILED***: '||SQLERRM);
raise;
end;
...
PROCEDURE test_constructor_with_id is
o_ base_;
begin
pl(chr(10)||'base_: test_constructor_with_id');
o_ := new base_(1);
pl('base_: test_constructor_with_id passed.');
exception
when OTHERS then
pl('base_: test_constructor_with_id ***FAILED***: '||SQLERRM);
raise;
end;
...
PROCEDURE test_sequence_name(
aiv_expected_name in varchar2) is
o_ base_;
v_sequence_name varchar2(100);
begin
pl(chr(10)||'base_: test_sequence_name');
o_ := new base_;
v_sequence_name := o_.sequence_name;
if v_sequence_name = aiv_expected_name then
pl('base_: test_sequence_name passed.');
else
pl('base_: test_sequence_name ***FAILED***: v_sequence_name='||
v_sequence_name||' not '||aiv_expected_name);
end if;
exception
when OTHERS then
pl('base_: test_sequence_name ***FAILED***: '||SQLERRM);
raise;
end;
...
PROCEDURE test_table_name(
aiv_expected_name in varchar2) is
o_ base_;
v_table_name varchar2(100);
begin
pl(chr(10)||'base_: test_table_name');
o_ := new base_;
v_table_name := o_.table_name;
if v_table_name = aiv_expected_name then
pl('base_: test_table_name passed.');
else
pl('base_: test_table_name ***FAILED***: v_table_name='||
v_table_name||' not '||aiv_expected_name);
end if;
exception
when OTHERS then
pl('base_: test_table_name ***FAILED***: '||SQLERRM);
raise;
end;
...
PROCEDURE test_type_name(
aiv_expected_name in varchar2) is
o_ base_;
v_type_name varchar2(100);
begin
pl(chr(10)||'base_: test_type_name');
o_ := new base_;
v_type_name := o_.type_name;
if v_type_name = aiv_expected_name then
pl('base_: test_type_name passed.');
else
pl('base_: test_type_name ***FAILED***: v_type_name='||
v_type_name||' not '||aiv_expected_name);
end if;
exception
when OTHERS then
pl('base_: test_type_name ***FAILED***: '||SQLERRM);
raise;
end;
...
PROCEDURE test_get_id is
o_ base_;
n_id number;
begin
pl(chr(10)||'base_: test_get_id');
o_ := new base_;
n_id := o_.get_id;
if n_id is not NULL then
pl('base_: test_get_id passed.');
else
pl('base_: test_get_id ***FAILED***.');
end if;
exception
when OTHERS then
pl('base_: test_get_id ***FAILED***: '||SQLERRM);
raise;
end;
...
PROCEDURE test_help is
begin
pl(chr(10)||'base_: test_help');
base_.help;
pl('base_: test_help passed.');
exception
when OTHERS then
pl('base_: test_help ***FAILED***: '||SQLERRM);
raise;
end;
...
PROCEDURE test_to_varchar2(
aiv_expected_value in varchar2) is
o_ base_;
v_map_value varchar2(100);
begin
pl(chr(10)||'base_: test_map_value');
o_ := new base_(1234567890);
v_map_value := '"'||o_.to_varchar2||'"';
if v_map_value = aiv_expected_value then
pl('base_: test_map_value passed.');
else
pl('base_: test_map_value ***FAILED***: v_map_value='||v_map_value||' not '||aiv_exp
end if;
exception
when OTHERS then
pl('base_: test_map_value ***FAILED***: '||SQLERRM);
raise;
end;
end test_base_;
/
show errors package body test_base_;
SQL> exec test_base_.test();
base_: test_constructor
base_: CONSTRUCTOR FUNCTION base_()
base_: test_constructor passed.
base_: test_constructor_with_id
base_: CONSTRUCTOR FUNCTION base_(id)
base_: test_constructor_with_id passed.
base_: test_sequence_name
base_: CONSTRUCTOR FUNCTION base_()
base_: MEMBER FUNCTION sequence_name()
base_: MEMBER FUNCTION type_name()
base_: test_sequence_name passed.
base_: test_table_name
base_: CONSTRUCTOR FUNCTION base_()
base_: MEMBER FUNCTION table_name()
base_: MEMBER FUNCTION type_name()
base_: test_table_name passed.
...
base_: test_type_name
base_: CONSTRUCTOR FUNCTION base_()
base_: MEMBER FUNCTION type_name()
base_: test_type_name passed.
base_: test_help
base_: MEMBER PROCEDURE help()
No help at this time.
base_: test_help passed.
base_: test_map_value
base_: CONSTRUCTOR FUNCTION base_(id)
base_: MAP MEMBER FUNCTION to_varchar2()
base_: test_map_value passed.
Document, Advertize, and Educate
 Use Wiki-based documentation
– it's accessible
– it's version controlled
– and then everyone's responsible for documentation
 Don’t punish people for errors, instead reward them for test unit coverage
 Share your best practices
 My book, Beginning PL/SQL: From Novice to ProfessionalBeginning PL/SQL: From Novice to Professional, has extensive coverage
of this topic. Besides, I really do need a vacation.
Closing Thoughts
 Objects (TYPEs) better model the real world, and hence provide a better solution
 Well though out use inheritance can significantly reduce the amount of code to
write, the time it takes to write it, and the time it takes to maintain it
 Using objects provides better consistency, in turn, better consistency provide
higher quality
 In an object-relational setting Packages are better suited as role players that
orchestrate the use of objects
 Or perhaps, those roles should be objects too?
 You can (almost) never test too much.
References
 Beginning PL/SQL: From Novice to Professional by Donald J. Bales (APress)
 Java Programming with Oracle JDBC by Donald J. Bales (O'Reilly)
 Oracle® Database PL/SQL Language Reference 11g Release 2 (11.2) (Oracle)
 Oracle® Database SQL Language Reference 11g Release 2 (11.2) (Oracle)
 Oracle® Database Object-Relational Developer's Guide 11g Release 2 (11.2) (Oracle)
 Oracle PL/SQL Programming by By Steven Feuerstein, Bill Pribyl (O'Reilly)
 Object-Oriented Technology: A Manager's Guide by David A. Taylor (Addison-Wesley)
 http://www.pl-sql.org
 http://technet.oracle.com
 http://www.apress.com/book/catalog?category=148
 http://oreilly.com/pub/topic/oracle

More Related Content

Viewers also liked

Partes Internas del Computador
Partes Internas del ComputadorPartes Internas del Computador
Partes Internas del Computador1511866
 
Sp rp wybory do pe
Sp rp wybory do peSp rp wybory do pe
Sp rp wybory do pep_andora
 
Purposeful_Painting_Book_of_Insights_Spring_2016_LinkedIn
Purposeful_Painting_Book_of_Insights_Spring_2016_LinkedInPurposeful_Painting_Book_of_Insights_Spring_2016_LinkedIn
Purposeful_Painting_Book_of_Insights_Spring_2016_LinkedInAlec Justice
 
online-left justified_resume updated
online-left justified_resume updatedonline-left justified_resume updated
online-left justified_resume updatedKenneth Warren
 
UKGBC: Better Places for People (GRESB Health and Well-being event, London, A...
UKGBC: Better Places for People (GRESB Health and Well-being event, London, A...UKGBC: Better Places for People (GRESB Health and Well-being event, London, A...
UKGBC: Better Places for People (GRESB Health and Well-being event, London, A...GRESB
 
Improving risk prevention in SMEs
Improving risk prevention in SMEsImproving risk prevention in SMEs
Improving risk prevention in SMEsINRSfrance
 
Right Conditions for Success / Adur & Worthing / Localgovcamp 2015
Right Conditions for Success / Adur & Worthing / Localgovcamp 2015Right Conditions for Success / Adur & Worthing / Localgovcamp 2015
Right Conditions for Success / Adur & Worthing / Localgovcamp 2015Paul Brewer
 
Bro, do you even know fonts?
Bro, do you even know fonts?Bro, do you even know fonts?
Bro, do you even know fonts?learningnight
 
Stephen bowerman professional curriculum vitae 2016
Stephen bowerman professional curriculum vitae 2016Stephen bowerman professional curriculum vitae 2016
Stephen bowerman professional curriculum vitae 2016Stephen Bowerman
 
Mobee_pop_pos_recognition_report_V1Q216
Mobee_pop_pos_recognition_report_V1Q216Mobee_pop_pos_recognition_report_V1Q216
Mobee_pop_pos_recognition_report_V1Q216Anthony Pisano, MS
 

Viewers also liked (15)

Partes Internas del Computador
Partes Internas del ComputadorPartes Internas del Computador
Partes Internas del Computador
 
Sp rp wybory do pe
Sp rp wybory do peSp rp wybory do pe
Sp rp wybory do pe
 
Purposeful_Painting_Book_of_Insights_Spring_2016_LinkedIn
Purposeful_Painting_Book_of_Insights_Spring_2016_LinkedInPurposeful_Painting_Book_of_Insights_Spring_2016_LinkedIn
Purposeful_Painting_Book_of_Insights_Spring_2016_LinkedIn
 
online-left justified_resume updated
online-left justified_resume updatedonline-left justified_resume updated
online-left justified_resume updated
 
UKGBC: Better Places for People (GRESB Health and Well-being event, London, A...
UKGBC: Better Places for People (GRESB Health and Well-being event, London, A...UKGBC: Better Places for People (GRESB Health and Well-being event, London, A...
UKGBC: Better Places for People (GRESB Health and Well-being event, London, A...
 
Improving risk prevention in SMEs
Improving risk prevention in SMEsImproving risk prevention in SMEs
Improving risk prevention in SMEs
 
Right Conditions for Success / Adur & Worthing / Localgovcamp 2015
Right Conditions for Success / Adur & Worthing / Localgovcamp 2015Right Conditions for Success / Adur & Worthing / Localgovcamp 2015
Right Conditions for Success / Adur & Worthing / Localgovcamp 2015
 
Researchass1
Researchass1Researchass1
Researchass1
 
OiRA 2
OiRA 2OiRA 2
OiRA 2
 
Census 2016 ''Banesat dhe Popullata Egjiptiane ne Elbasan''
Census 2016 ''Banesat dhe Popullata Egjiptiane ne Elbasan''Census 2016 ''Banesat dhe Popullata Egjiptiane ne Elbasan''
Census 2016 ''Banesat dhe Popullata Egjiptiane ne Elbasan''
 
Leyes de la entalpía
Leyes de la entalpíaLeyes de la entalpía
Leyes de la entalpía
 
Bro, do you even know fonts?
Bro, do you even know fonts?Bro, do you even know fonts?
Bro, do you even know fonts?
 
SPACE SHUTTLE & ITS TPS
SPACE SHUTTLE  & ITS TPSSPACE SHUTTLE  & ITS TPS
SPACE SHUTTLE & ITS TPS
 
Stephen bowerman professional curriculum vitae 2016
Stephen bowerman professional curriculum vitae 2016Stephen bowerman professional curriculum vitae 2016
Stephen bowerman professional curriculum vitae 2016
 
Mobee_pop_pos_recognition_report_V1Q216
Mobee_pop_pos_recognition_report_V1Q216Mobee_pop_pos_recognition_report_V1Q216
Mobee_pop_pos_recognition_report_V1Q216
 

Similar to Writing Maintainable Code

Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning CBuilding and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning CDavid Wheeler
 
Object oriented development with PL/SQL
Object oriented development with PL/SQLObject oriented development with PL/SQL
Object oriented development with PL/SQLDonald Bales
 
Object-oriented Development with PL-SQL
Object-oriented Development with PL-SQLObject-oriented Development with PL-SQL
Object-oriented Development with PL-SQLDonald Bales
 
Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)vilniusjug
 
Neo4j Stored Procedure Training Part 1
Neo4j Stored Procedure Training Part 1Neo4j Stored Procedure Training Part 1
Neo4j Stored Procedure Training Part 1Max De Marzi
 
utPLSQL: Unit Testing for Oracle PL/SQL
utPLSQL: Unit Testing for Oracle PL/SQLutPLSQL: Unit Testing for Oracle PL/SQL
utPLSQL: Unit Testing for Oracle PL/SQLSteven Feuerstein
 
New Ideas for Old Code - Greach
New Ideas for Old Code - GreachNew Ideas for Old Code - Greach
New Ideas for Old Code - GreachHamletDRC
 
SADI in Perl - Protege Plugin Tutorial (fixed Aug 24, 2011)
SADI in Perl - Protege Plugin Tutorial (fixed Aug 24, 2011)SADI in Perl - Protege Plugin Tutorial (fixed Aug 24, 2011)
SADI in Perl - Protege Plugin Tutorial (fixed Aug 24, 2011)Mark Wilkinson
 
Questioning the status quo
Questioning the status quoQuestioning the status quo
Questioning the status quoIvano Pagano
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Codeerikmsp
 
SELJE_Database_Unit_Testing_Slides.pdf
SELJE_Database_Unit_Testing_Slides.pdfSELJE_Database_Unit_Testing_Slides.pdf
SELJE_Database_Unit_Testing_Slides.pdfEric Selje
 
Weird Plsql
Weird PlsqlWeird Plsql
Weird Plsqlwebanddb
 
Developer Tests - Things to Know
Developer Tests - Things to KnowDeveloper Tests - Things to Know
Developer Tests - Things to KnowVaidas Pilkauskas
 
Back-2-Basics: .NET Coding Standards For The Real World
Back-2-Basics: .NET Coding Standards For The Real WorldBack-2-Basics: .NET Coding Standards For The Real World
Back-2-Basics: .NET Coding Standards For The Real WorldDavid McCarter
 
Unit I Advanced Java Programming Course
Unit I   Advanced Java Programming CourseUnit I   Advanced Java Programming Course
Unit I Advanced Java Programming Courseparveen837153
 

Similar to Writing Maintainable Code (20)

Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning CBuilding and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
 
Object oriented development with PL/SQL
Object oriented development with PL/SQLObject oriented development with PL/SQL
Object oriented development with PL/SQL
 
Object-oriented Development with PL-SQL
Object-oriented Development with PL-SQLObject-oriented Development with PL-SQL
Object-oriented Development with PL-SQL
 
Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)
 
Bioinformatica 10-11-2011-p6-bioperl
Bioinformatica 10-11-2011-p6-bioperlBioinformatica 10-11-2011-p6-bioperl
Bioinformatica 10-11-2011-p6-bioperl
 
Neo4j Stored Procedure Training Part 1
Neo4j Stored Procedure Training Part 1Neo4j Stored Procedure Training Part 1
Neo4j Stored Procedure Training Part 1
 
utPLSQL: Unit Testing for Oracle PL/SQL
utPLSQL: Unit Testing for Oracle PL/SQLutPLSQL: Unit Testing for Oracle PL/SQL
utPLSQL: Unit Testing for Oracle PL/SQL
 
New Ideas for Old Code - Greach
New Ideas for Old Code - GreachNew Ideas for Old Code - Greach
New Ideas for Old Code - Greach
 
2012 03 08_dbi
2012 03 08_dbi2012 03 08_dbi
2012 03 08_dbi
 
SADI in Perl - Protege Plugin Tutorial (fixed Aug 24, 2011)
SADI in Perl - Protege Plugin Tutorial (fixed Aug 24, 2011)SADI in Perl - Protege Plugin Tutorial (fixed Aug 24, 2011)
SADI in Perl - Protege Plugin Tutorial (fixed Aug 24, 2011)
 
Sql optimize
Sql optimizeSql optimize
Sql optimize
 
Questioning the status quo
Questioning the status quoQuestioning the status quo
Questioning the status quo
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Code
 
SELJE_Database_Unit_Testing_Slides.pdf
SELJE_Database_Unit_Testing_Slides.pdfSELJE_Database_Unit_Testing_Slides.pdf
SELJE_Database_Unit_Testing_Slides.pdf
 
Weird Plsql
Weird PlsqlWeird Plsql
Weird Plsql
 
Developer Tests - Things to Know
Developer Tests - Things to KnowDeveloper Tests - Things to Know
Developer Tests - Things to Know
 
Back-2-Basics: .NET Coding Standards For The Real World
Back-2-Basics: .NET Coding Standards For The Real WorldBack-2-Basics: .NET Coding Standards For The Real World
Back-2-Basics: .NET Coding Standards For The Real World
 
Unit I Advanced Java Programming Course
Unit I   Advanced Java Programming CourseUnit I   Advanced Java Programming Course
Unit I Advanced Java Programming Course
 
Bioinformatics p5-bioperlv2014
Bioinformatics p5-bioperlv2014Bioinformatics p5-bioperlv2014
Bioinformatics p5-bioperlv2014
 
Bioinformatica p6-bioperl
Bioinformatica p6-bioperlBioinformatica p6-bioperl
Bioinformatica p6-bioperl
 

Writing Maintainable Code

  • 1. Writing Maintainable Code by Donald J. Bales (630) 776-0071 don@donaldbales.com http://www.pl-sql.org http://www.donaldbales.com http://www.facebook.com/donald.j.bales http://www.linkedin.com/in/donaldbales
  • 2. Please buy these, I’d need a vacation!
  • 3. Audience Assessment  Who has had some experience with object-orientation?  Who has done some object-oriented programming?  PL/SQL?  Who has used CREATE TYPE… AS OBJECT?  Who has used CREATE TABLE … OF…  Who knows some Sixties-speak?
  • 6. Stewardship “Inspect what you expect” (Ziglar)  Everyone must participate, but you’ll only have a maintainable code base if someone makes sure you have a maintainable code base.  Use a source control system – Hint: “The data-dictionary in an Oracle database is not a source control system!”
  • 7. The Organizing Principle  All decisions must evolve around a core principal, in this case, writingAll decisions must evolve around a core principal, in this case, writing maintainable codemaintainable code. Not two or three principals, just one! So what is maintain-able code?  No or low defects – that way there’s less to maintain  A flexible architecture; expansion capabilities – so when you’re asked to make it do something additional, you don’t have to start over. “Plan for the future, where every one always wants more.”  Modular coding – well defined interfaces mean less internal interaction between components  Consistency – so when changes do need to be made, it’s easy. “It's better to be consistently wrong than it is to be inconsistent.”  Built-in troubleshooting – when something does go wrong, it’s easy to detect  Built-in performance profiling – when it’s not expanding well, it’s easy to detect  Use object-orientation – better taste and less filling
  • 8. Organizing Your Source Code  Break scripts into separate files  Make scripts atomic  Make scripts re-runnable  Agree upon and follow naming conventions (stewardship)  Name files after the object they will create “make it obvious” Example: genders.tab for a table of gender codes Script Type Suffix Stand-alone functions .fun Stand-alone procedures .prc Type specifications .tps Type bodies .tpb Tables (includes column and tables constraints, sequence, indexes) .tab Package specifications .pks Package bodies .pkb Views .vw Inserts .ins Updates .upd Deletes .del Selects .sql
  • 9. SQL
  • 10. Naming Conventions In a Relational Setting…  Use plural table names, after all, they are collections  Create a sequence with the same name as its associate table, with a suffix of _ID – Example: a table named GENDERS would have a corresponding sequence named GENDER_ID. Why? “Then it’s obvious.”  Use singular package names  Create a package for each table’s associated behavior – Example: a table named GENDERS would have a corresponding package named GENDER. Why? Atomicity and modularity. If you need to fix something in the GENDER package, only it will be affected. Object Type Object Name Table GENDERS Sequence GENDER_ID (table) Package GENDER
  • 11. Naming Conventions In an Object-relational Setting…  Use singular type names  Create tables for each type’s persistence – Example: a type named GENDER would have a corresponding table named GENDERS. Why? “Then it’s obvious.”  Use plural table names, after all, they are collections  Create a sequence with the same name as its associate table, with a suffix of _ID – Example: a table named GENDERS would have a corresponding sequence named GENDER_ID. Why? “Then it’s obvious.” Object Type Object Name Table GENDERS Sequence GENDER_ID Type GENDER
  • 12. Naming Conventions Consistency  Use the same name for something at every layer, after all it’s the same thing! – column -> parameter -> variable, table -> cursor -> record – attribute -> parameter -> variable, type -> table -> cursor -> record In other words, none of this %^&*: birth, birth_date, date_of_birth, born, born_on, bday (ahhh, your making me crazy!)  Few or no abbreviations – they create ambiguity
  • 13. Use Constraints “garbage in, garbage out”  Use check constraints – not null – date = trunc(date) if time is not used needed – number = trunc(number) for integers, why?  Use primary keys against an id  Use unique keys against real-world primary key values  Use foreign keys – Really. I’m not kidding.  Use triggers to implement conditional foreign keys create table TEST_INTEGER (an_integer integer); insert into TEST_INTEGER values (1.5); insert into TEST_INTEGER values (1.6); select * from TEST_INTEGER; AN_INTEGER ---------- 1 2
  • 14. Plan For Change  Normalize your types and tables, don't “de-normalize for performance” – denormalization hinders expansion without modification  Don't constrain numbers – things are always getting larger and more expensive  Give varchar2 attributes and columns plenty of room -- things are always getting larger and being internationalized. You’d be surprised how compact the English language is when compared to others  Use CLOBS instead of varchar2(4000) when appropriate – they are now well supported by all layers of development  use the AL32UTF8 character set
  • 15. Plan for Re-Use  Design with re-use in mind  Use polymorphic naming – come up with a set of common names before coding; minimize they set of names used  Inheritance v. code generation – Inheritance fosters consistency, efficiency, and modularity  Encapsulation improves modularity; modularity reduces interdependence; less interdependence means better maintainability  Did I say use the AL32UTF8 character set?
  • 16. Look for the “Gotchas”  Never use = NULL= NULL in a SQL statement, because NULL is not equal to anything, not even NULL  Search you source code for the above mistake  Never use as UPDATE statement without detectionUPDATE statement without detection, i.e. a WHERE clause that makes sure you are updating something that has not already been updated
  • 18. Prevention and Preparation  I like to think of maintainability in PL/SQL in two facets: – Prevention – Preparation  First let’s look at prevention – Naming Conventions – Anchors – Explicit Conversion – Blocking  And then, preparation – Comments – Blocking – Success and Failure Messages – Built-in Troubleshooting Capabilities – Built-in Profiling Capabilities
  • 19. Naming Conventions in PL/SQL  Use data-type prefixes (or suffixes). Why?  Parameter prefixes: – ai – argument IN – aio – argument IN OUT – ao – argument OUT  For example: Data Type Prefix Argument/Parameter a_ Cursors c_ Dates d_ Numbers n_ Objects o_ Records/Rows r_ Tables/Types t_ Varchar2 v_ Access Modifiers IN i IN OUT io OUT o PROCEDURE get_code_id_description( aiov_code in out varchar2, aon_id out number, aov_description out varchar2, aid_on in date ) is v_code varchar2(30); v_table_name varchar2(100); begin ... end get_code_id_description;
  • 20. *Anchoring Data Types in PL/SQL  *The term anchoring originated with Steven.  Use %TYPE for attributes and columns  Use %ROWTYPE for records and rows  Why? You can use the wrong data type. And, it makes it obvious!
  • 21. Conversions in PL/SQL  Use explicit conversions where you wrap the conversion in a PL/SQL block in order to detect a failure to convert a character value to a number or date.  Wrap null-able attributes or columns variables in NVL() with a well known substitution value so your IF statements don’t fail silently.
  • 22. Catching Errors in PL/SQL  Block (BEGIN … EXCEPTION … END;BEGIN … EXCEPTION … END;) every SQL statement [except a cursor?]  Block all singletons. Oh yeah. I already stated this above, didn’t I. Must be Important?  Block all character to number conversions  Block all character to date conversions  Never use WHEN OTHERS then NULL;WHEN OTHERS then NULL; (OK. On a rare occasion I do.) Search your source code for this and make sure it really needs to exist!  Add PROCEDURE initialize()PROCEDURE initialize() to every package that uses its initialization section, in order to exercise its initialization section on demand. -- This is the cause of the error that happens once and then mysteriously goes away! BEGIN select description into v_description from GENDER; EXCEPTION WHEN NO_DATA_FOUND then v_description := ' '; WHEN OTHERS then NULL; -- It'll take you hours to find this insect. END;
  • 23. create or replace PACKAGE test_initialization_section as PROCEDURE hello; PROCEDURE initialize; end test_initialization_section; / create or replace PACKAGE BODY test_initialization_section as v_greeting varchar2(4); PROCEDURE hello is begin pl(v_greeting); end hello; PROCEDURE initialize is begin v_greeting := 'Hello'; end initialize; -- THE INITIALIZATION SECTIONTHE INITIALIZATION SECTION begin initializeinitialize; end test_initialization_section; / test_initialization_section.hello code…
  • 24. test_initialization_section.hello output… BEGIN test_initialization_section.hello.hello; END; * ERROR at line 1: ORA-06502: PL/SQL: numeric or value error: character string buffer too small ORA-06512: at "BPS.TEST_INITIALIZATION_SECTION", line 15 ORA-06512: at "BPS.TEST_INITIALIZATION_SECTION", line 20 ORA-06512: at line 1 BEGIN test_initialization_section.hello.hello; END; PL/SQL procedure successfully completed. BEGIN test_initialization_section.hello.hello; END; PL/SQL procedure successfully completed. SQL> exec test_initialization_section.initialize.initialize; BEGIN test_initialization_section.initialize; END; * ERROR at line 1: ORA-06502: PL/SQL: numeric or value error: character string buffer too small ORA-06512: at "BPS.TEST_INITIALIZATION_SECTION", line 15 ORA-06512: at line 1
  • 25. Comments in PL/SQL  Explain mysterious code  Document every type specification  Document every package specification  Document other functions and procedures  Add PROCEDURE help(); to every type and package specification. Have it extract you comments from your types and packages by querying SYS.DBA_SOURCESYS.DBA_SOURCE.  Produce distributable documentation – so somebody else does have to code the same thing, because they don’t know it already exists! Name Null? TypeName Null? Type ------------------------------- -------- ---------------------- OWNER VARCHAR2(30) NAME VARCHAR2(30) TYPE VARCHAR2(12) LINE NUMBER TEXT VARCHAR2(4000)
  • 26.
  • 27.
  • 28.
  • 29.
  • 30. Error Handling in PL/SQL  Block (BEGIN … EXCEPTION … END;BEGIN … EXCEPTION … END;) every SQL [statement except a cursor?]  Only use an UPDATE statement with detectionUPDATE statement with detection, i.e. with a WHERE clause that makes sure you are updating something that has not already been updated!  Block all singletons. Oh yeah. I already stated this above, and again? Important?  Block all character to number conversions  Block all character to date conversions  Every unhandled exception should raise a failure (error) message  Use well formatted and well know success and failure messages  I use ORA-20000 – ORA-200## starting from zero at the top of a type or package, incrementing by one for each new exception clause.  I use a standard format like this: when OTHERS then raise_application_error(-20000, SQLERRM|| ' on SELECT GENDERS'|| ' in GENDER.get(aiv_code)'); end;
  • 31. Build-in Trouble-shooting Capability  Add boolean b_debugboolean b_debug and then use if (b_debug)if (b_debug) for every built-in message  Add a PROCEDURE set_debug_on()PROCEDURE set_debug_on() and PROCEDURE set_debug_off()PROCEDURE set_debug_off() to your packages in order to turn debug logging on and off on demand  Use SYS.DBMS_OUTPUT.put_line()SYS.DBMS_OUTPUT.put_line() to print debug messages to the console  Or better yet, create a DEBUGDEBUG type or package that logs your if (b_debug)if (b_debug) messages to a DEBUGSDEBUGS table, so you can view your PL/SQL progam’s progress as it happens by querying the DEBUGSDEBUGS table.  Further, create a DEBUGDEBUG package that sets the debug state for each PL/SQL program in a table that is in turn queried by a given package on start-up  Dumb down your code by declaring intermediate variablesdeclaring intermediate variables that can be viewed by a debugger  I suggest you also learn how to use the debugger!
  • 32. Build-in Performance Profiling Capabilities  Use Explain Plan on your SQL statements  Keep track of the start and stop times of your functions and procedures and log the output to a profiling table  Use SYS.DBMS_PROFILER to profile your PL/SQL programs
  • 34. Write Test Plans “It doesn't work until you prove it to me!”  Think about how something should work, and plan on testing it—or better yet, write the test first!  On average, 40% of my code is defective the first time I run a well written test unit against it! And yet, I try so hard to prevent any errors!!!  Track defects, categorize them, and then update your test plan and then test units to make sure you test for them in the future
  • 35. Write Test Units (first?) “Ah! The proof!”  Strive for 100% coverage100% coverage, it’s achievable in this development layer  Write a test package for every type and packagetest package for every type and package  Name the test package after the package or type it tests, just prefix the name with TEST_TEST_  Write one or more test units for each method: function or procedure.  Name each test unit after the method it tests, just prefix the method name with, guess what? TEST_TEST_  Log both success and failure to the console or to a TESTSTESTS table. I personally like a table because I can more easily do a statistic analysis on my defects.  And yes, even write test units for the Oracle packages you useyes, even write test units for the Oracle packages you use. You know how they’re supposed to work because you have some documentation—but do they?  Write an automated testing package to query the database for all your test units and run them allrun them all any time you make a change to the source code!
  • 36. create or replace TYPE base_ as object ( id number, CONSTRUCTOR FUNCTION base_( self in out nocopy base_) return self as result deterministic, CONSTRUCTOR FUNCTION base_( self in out nocopy base_, id number) return self as result deterministic, MEMBER FUNCTION sequence_name return varchar2, MEMBER FUNCTION table_name return varchar2, MEMBER FUNCTION type_name return varchar2, MEMBER FUNCTION get_id return number, MEMBER PROCEDURE save, MAP MEMBER FUNCTION to_varchar2 return varchar2, STATIC PROCEDURE help ) not final; / show errors type base_;
  • 37. create or replace package body test_base_ as PROCEDURE test is begin test_constructor; test_constructor_with_id; test_sequence_name('BASE__ID'); test_table_name('BASE_S'); test_type_name('BASE_'); test_help; test_to_varchar2('"00000000000000000000000000001234567890"'); end; PROCEDURE test_constructor is o_ base_; begin pl(chr(10)||'base_: test_constructor'); o_ := new base_; pl('base_: test_constructor passed.'); exception when OTHERS then pl('base_: test_constructor ***FAILED***: '||SQLERRM); raise; end; ...
  • 38. PROCEDURE test_constructor_with_id is o_ base_; begin pl(chr(10)||'base_: test_constructor_with_id'); o_ := new base_(1); pl('base_: test_constructor_with_id passed.'); exception when OTHERS then pl('base_: test_constructor_with_id ***FAILED***: '||SQLERRM); raise; end; ...
  • 39. PROCEDURE test_sequence_name( aiv_expected_name in varchar2) is o_ base_; v_sequence_name varchar2(100); begin pl(chr(10)||'base_: test_sequence_name'); o_ := new base_; v_sequence_name := o_.sequence_name; if v_sequence_name = aiv_expected_name then pl('base_: test_sequence_name passed.'); else pl('base_: test_sequence_name ***FAILED***: v_sequence_name='|| v_sequence_name||' not '||aiv_expected_name); end if; exception when OTHERS then pl('base_: test_sequence_name ***FAILED***: '||SQLERRM); raise; end; ...
  • 40. PROCEDURE test_table_name( aiv_expected_name in varchar2) is o_ base_; v_table_name varchar2(100); begin pl(chr(10)||'base_: test_table_name'); o_ := new base_; v_table_name := o_.table_name; if v_table_name = aiv_expected_name then pl('base_: test_table_name passed.'); else pl('base_: test_table_name ***FAILED***: v_table_name='|| v_table_name||' not '||aiv_expected_name); end if; exception when OTHERS then pl('base_: test_table_name ***FAILED***: '||SQLERRM); raise; end; ...
  • 41. PROCEDURE test_type_name( aiv_expected_name in varchar2) is o_ base_; v_type_name varchar2(100); begin pl(chr(10)||'base_: test_type_name'); o_ := new base_; v_type_name := o_.type_name; if v_type_name = aiv_expected_name then pl('base_: test_type_name passed.'); else pl('base_: test_type_name ***FAILED***: v_type_name='|| v_type_name||' not '||aiv_expected_name); end if; exception when OTHERS then pl('base_: test_type_name ***FAILED***: '||SQLERRM); raise; end; ...
  • 42. PROCEDURE test_get_id is o_ base_; n_id number; begin pl(chr(10)||'base_: test_get_id'); o_ := new base_; n_id := o_.get_id; if n_id is not NULL then pl('base_: test_get_id passed.'); else pl('base_: test_get_id ***FAILED***.'); end if; exception when OTHERS then pl('base_: test_get_id ***FAILED***: '||SQLERRM); raise; end; ...
  • 43. PROCEDURE test_help is begin pl(chr(10)||'base_: test_help'); base_.help; pl('base_: test_help passed.'); exception when OTHERS then pl('base_: test_help ***FAILED***: '||SQLERRM); raise; end; ...
  • 44. PROCEDURE test_to_varchar2( aiv_expected_value in varchar2) is o_ base_; v_map_value varchar2(100); begin pl(chr(10)||'base_: test_map_value'); o_ := new base_(1234567890); v_map_value := '"'||o_.to_varchar2||'"'; if v_map_value = aiv_expected_value then pl('base_: test_map_value passed.'); else pl('base_: test_map_value ***FAILED***: v_map_value='||v_map_value||' not '||aiv_exp end if; exception when OTHERS then pl('base_: test_map_value ***FAILED***: '||SQLERRM); raise; end; end test_base_; / show errors package body test_base_;
  • 45. SQL> exec test_base_.test(); base_: test_constructor base_: CONSTRUCTOR FUNCTION base_() base_: test_constructor passed. base_: test_constructor_with_id base_: CONSTRUCTOR FUNCTION base_(id) base_: test_constructor_with_id passed. base_: test_sequence_name base_: CONSTRUCTOR FUNCTION base_() base_: MEMBER FUNCTION sequence_name() base_: MEMBER FUNCTION type_name() base_: test_sequence_name passed. base_: test_table_name base_: CONSTRUCTOR FUNCTION base_() base_: MEMBER FUNCTION table_name() base_: MEMBER FUNCTION type_name() base_: test_table_name passed. ...
  • 46. base_: test_type_name base_: CONSTRUCTOR FUNCTION base_() base_: MEMBER FUNCTION type_name() base_: test_type_name passed. base_: test_help base_: MEMBER PROCEDURE help() No help at this time. base_: test_help passed. base_: test_map_value base_: CONSTRUCTOR FUNCTION base_(id) base_: MAP MEMBER FUNCTION to_varchar2() base_: test_map_value passed.
  • 47. Document, Advertize, and Educate  Use Wiki-based documentation – it's accessible – it's version controlled – and then everyone's responsible for documentation  Don’t punish people for errors, instead reward them for test unit coverage  Share your best practices  My book, Beginning PL/SQL: From Novice to ProfessionalBeginning PL/SQL: From Novice to Professional, has extensive coverage of this topic. Besides, I really do need a vacation.
  • 48. Closing Thoughts  Objects (TYPEs) better model the real world, and hence provide a better solution  Well though out use inheritance can significantly reduce the amount of code to write, the time it takes to write it, and the time it takes to maintain it  Using objects provides better consistency, in turn, better consistency provide higher quality  In an object-relational setting Packages are better suited as role players that orchestrate the use of objects  Or perhaps, those roles should be objects too?  You can (almost) never test too much.
  • 49. References  Beginning PL/SQL: From Novice to Professional by Donald J. Bales (APress)  Java Programming with Oracle JDBC by Donald J. Bales (O'Reilly)  Oracle® Database PL/SQL Language Reference 11g Release 2 (11.2) (Oracle)  Oracle® Database SQL Language Reference 11g Release 2 (11.2) (Oracle)  Oracle® Database Object-Relational Developer's Guide 11g Release 2 (11.2) (Oracle)  Oracle PL/SQL Programming by By Steven Feuerstein, Bill Pribyl (O'Reilly)  Object-Oriented Technology: A Manager's Guide by David A. Taylor (Addison-Wesley)  http://www.pl-sql.org  http://technet.oracle.com  http://www.apress.com/book/catalog?category=148  http://oreilly.com/pub/topic/oracle