comp61511 (fall 2017) software engineering concepts in...

Post on 31-Mar-2020

3 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

1

COMP61511(Fall2017)SoftwareEngineeringConcepts

InPracticeWeek2

BijanParsia&ChristosKotselidis< , @manchester.ac.uk>

(bugreportswelcome!)bijan.parsia christos.kotselidis

2.1

FizzBuzzInWayTooMuchDetail

2.2

TheNaivestFizzbuzzAnyproposals?Let'sseethe !obvious

2.3

ARationalFizzBuzzLet'sconsidera"standard"implementation

NotsillyNotgolfy

I.e.,asimplelooporientedimplementation

2.4

DRY"Don'tRepeatYourself"

AfundamentalprincipleofSEItisagainst

CutandPastereuseNotInventedHeresyndrome

Isour DRY?currentversion

2.5

ADryerVersionWerepeati % 3 == 0andi % 5 == 0Let's !abstractthatout

2.6

EVENDRIER!!!Werepeatthe_ % _ == 0pattern!WesayprintalotWecan !fixit

2.7

ParameterizationBasicsoftwareprinciple:Don'thardcodestuff!

Makeyourcodeparameterisable!Thecurrentversionhardcodesalot,e.g.,

Wehavetomodifythesourcecodeifwewanttochangethis!Whatelseishardcoded?Wecan !

FIZZ = 'Fizz'BUZZ = 'Buzz'

fixit

2.8

StillHardCoding!ThekindoftestishardcodedWecanfix !that

2.9

ThePathToHell......ispavedwithgoodintentions!Eachchoicewassomehowreasonable

WeappliedgoodSEprinciplesWemadechoicesthatareoftengood

ButweendedupinnonsenselandLocalsenseledtoglobalnonsense

2.10

JudgementSoftwareengineerscan'tjustfollowrulesGoodsoftwareengineeringrequiresjudgementWhentoapplywhichrulesWhentobreakrules*HowtoapplyorbreakthemThereasonforeachrule

Andwhetheritmakessensenow

2.11

AcknowledgementThislecturewasderivedfromtheexcellentblogpost

byTomDalling.FizzBuzzInTooMuch

Detail

TomusesRubyandgoesacoupleofstepsfurther.Wortharead!

3.2

WhoOwnsYourCode?Youwrotesomecode!

Allweek!Bothsystemsandtests!

Akeyquestion:Whoownsthatcode?

Ordifferentbitsofit?Whatkindofownership?

3.3

IntellectualProperty(IP)Intellectualproperty is anyarticulable, tangible productionofamindwhosephysical realisationsare restrictedby law (inproduction,distribution,etc.)

Wedon'tcontrolwhatotherpeoplethink!Wecancontrolwhattheydowithcertainthoughts.IntellectualPropertyrightsgivepowertocertainpeopletocontrolwhatotherpeopledo

Forexample,whethertheycandistributeabook,song,orprogram

3.4

KindsOfIntellectualPropertyName Establishment Enforcement

Copyright Automatic,immediate CivilandCriminal

Patent Application;exposurebeforeapplicationdestroysit

Mostlycivil

Trademark Applicationandvigorousdefense MostlycivilTradeSecret

Automatic(bynottellingpeople)andNDAs Mostlycivil

3.5

CopyrightCopyright isa licensablemonopolyoftangible expressionofan idea with respect to reproduction, derivation, display,distribution,andthelike.

ProtectstheexpressionnottheideaThoughthesebluratthelimit

Someplagiarismisacopyrightviolation;someisnotTypicallyautomaticallyassignedatcreationtime

No"notice"or"registration"neededThoughthesemighthelpwithlawsuits

3.6

PatentsApatent isa licensablemonopolyof theuseorsaleofa"non-obvious"invention(ofaprocess,machine,design(sometimes),mechanism,procedure,etc.

ApatentisanincentivetodiscloseManypatentableinventionscouldbeexploited"secretly"Goalistoaddtoourcommonknowledge

PriorartdestroysapatentIncludingyourown

Defensivepatenting"common"Independentinventionnodefense

3.7

TradeSecretAtradesecretisaninventionwhichisnotdisclosed

PersistsforeverUnlessleakedOrreinvented

TypicallyprotectedbysecrecyOrspecificcontracts

"Non-DisclosureAgreements"(NDAs)

3.8

WhoOwnsYourCode?Copyrightstartswiththecreator

I.e.,you!Cheap!(Eventoregister)Unlessyoucreateitaswork-for-hire

OrotherwisetransferitPatentsbelongtothepatenter

Expensive(ish)tosecureTradesecretsbelongtotheinventor

AreYouWorkingForHire?Not !quite

3.9

3.10

WhatToKeepInMind(Now)SoftwareengineerstypicallyproduceIP

Evenifnotprotected,ouroutputis"intellectual"VariousformsofIPdriveproductvalueemployee/entrepreneurvalue

SoftwareengineerstypicallyuseIPAllsortsandinallwaysIPconsiderationsaconstraintonthedesignspace

4.1

ComprehendingProductQualities

4.2

Comprehension?Wecandistinguishtwoforms:

Know-thatYoubelieveatrueclaimaboutthesoftware...withappropriateevidence

Know-howYouhaveacompetancywithrespecttothesoftwareE.g.,youknow-howtorecompileitforadifferentplatform

TheyareinterrelatedBothrequiresignificanteffort!

4.3

QualityLevelsWetalkedaboutdifferentkindsofquality

Butforeachkindtherecanbedegreesorlevelsthereof"Easy"example:Highvs.Lowperformance

MostqualitiesinprinciplearequantifiableMostthingsarequantifiableinsomesense

Butreasonablequantificationisn'talwayspossibleOrworthitBeingclearaboutyourvaguenessisessential!

4.4

ClarityOurdiscussionwillbeadequate if ithasasmuchclearnessas the subject-matter admits of, for precision is not to besought for alike in all discussions, any more than in all theproducts of the crafts...for it is the mark of an educated[person]tolookforprecisionineachclassofthingsjustsofarasthenatureofthesubjectadmits...—Aristotle,NicomachaenEthics,Book1,3

4.5

Clarity(2)We demand rigidly defined areas of doubt anduncertainty!—DouglasAdams,TheHitchhiker'sGuidetotheGalaxy

4.6

DefectsAsQualityLacksA defect in a software system is a quality level (for somequality)thatisnotacceptable.

QualitylevelsneedtobeelicitedandnegotiatedAllpartiesmustagreeonwhattheyare,theiroperationaldefinitiontheirsignificance

Whatcountsasadefectisoftendeterminedlateinthegame!

4.7

QuestionIfyourprogramcrashesthenit

1. definitelyhasabug.2. ishighlylikelytohaveabug.3. mayormaynothaveabug.

4.8

QuestionIfyourprogramcrashes,andthecauseisinyourcode,thenit

1. definitelyhasabug.2. ishighlylikelytohaveabug.3. mayormaynothaveabug.

4.9

BugOrFeature?( —scrollforthecartoonsaswellasthewisdom.)DoesQAhateyou?

Evenacrashingcodepathcanbeafeature!Contentionariseswhenthestakesarehigh

andsometimethestakescanseemhightosomepeople!defectrectificationcoststhesame

whetherthedefectisdetected......orafeatureisredefined

Defects(evenredefinedfeatures)aren'tpersonal

4.10

4.11

ProblemDefinitionThe penalty for failing to define the problem is that you canwastealotoftimesolvingthewrongproblem.Thisisadouble-barreledpenaltybecauseyoualsodon'tsolvetherightproblem.—McConnell,3.3

4.12

QualityAssuranceDefectAvoidanceorPrevention

"Prerequisite"workcanhelpRequirementnegotiationDesignTechchoice

MethodologyDefectDetection&Rectification

Ifadefectexists,FinditFixit

4.13

ThePointsOfQuality1. Defectprevention

Designcare,codereviews,etc.2. Defectappraisal

Detection,triaging,etc.3. Internalrectification

Wefix/mitigatebeforeshipping4. Externalrectification

Wecopeaftershipping

4.14

DefectDetectionTechniques

4.16

ExperiencingSoftwareIt'sonetoknowthattherearebugsAllsoftwarehasbugs!

It'sanothertobeabletotriggerabugNotjustaspecificbug!Ifyouunderstandthesoftware

Youknowhowtobreakit.Similarly,formakingchanges

tweaks,extensions,adaptions,etc.Themorecommand,themoremodalitiesofmastery

4.17

FormsOfKnowledge(Manifestations)Humaninterpretable

Comments,designdocs,userstories,javadocSourcecode

Both,awrittendescriptionanda"live"objectAlsothingslikedemocode,examples,testsuites,ec.

Diagrams"Mere"picturestosemi-formaltoformaldiagrams:ERdocs,UML,etc.

FormalspecificationsCompetencies

Icanmakeitcrash

4.18

SourcesOfKnowledge(Modalities)Analyticalknowledge

DerivedfrominspectionandreasoningCanbeautomatedusingformalmethods

ExperimentalknowledgeDerivedfromtheconductofexperimentsTypicallytests

ExperientialknowledgeDerivedfrompersonalinteractionwiththesoftwareStrong"know-how"component

5.2

RevisitingRainfallWe'regoingtolookatyourrainfallsbeforediscussingitindetail.

We'regoingtodoacodereview!

You'regoingtoworkin2-personteams!

5.3

ThreeTasks1. Doacodereview!2. Writesometestsbasedonyourcodereview!3. Doanessayreview!

Tothelab!Materialintheusualplace.

6.2

RainfailKeypoint:0outof47programspassedall13tests

1programpassed8tests2passed75passed60passed5or45passed33passed21passed115passed011"hadaproblemwithsubmission"4"Wecouldnotcompileyourcode."

Therainfallproblemisstillachallenge!

6.3

Let'sTalkTestingYouhadlimitedtime

Sotestgenerationhadtobequick!Typicallyadhoc

Canwedobetter?Howtestableisrainfall.py?

Youwereresponsibleonlyforaverage_rainfall(input_list)Onlythisunit!Canignoreallelse!

Perfectfordoctest

6.4

ProblemStatementDesign a program called rainfall that consumes a list ofnumbers representing daily rainfall amounts as entered by auser. The list may contain the number -999 indicating theend of thedataof interest.Produce theaverageof thenon-negativevalues in the listup to the first -999(if it showsup).Theremaybenegativenumbersotherthan-999inthelist.

6.5

SetUpdef average_rainfall(input_list): """>>> average_rainfall(<<FIRST TEST INPUT>>) <<FIRST EXPECTED RESULT>> """ # Here is where your code should go return "Your computed average as a integer" #<-- change this!

$ python 1setup.py Your computed average as a integer

6.6

FirstTestRun$ python -m doctest 1setup.py **********************************************************************File "/Users/bparsia/Documents/2018/Teaching/COMP61511/labs/lab1/followup/1setup.py", line 2, in 1setup.average_rainfallFailed example: average_rainfall(<<FIRST TEST INPUT>>)Exception raised: Traceback (most recent call last): File "//anaconda/lib/python3.5/doctest.py", line 1320, in __run compileflags, 1), test.globs) File "<doctest 1setup.average_rainfall[0]>", line 1 average_rainfall(<<FIRST TEST INPUT>>) ^ SyntaxError: invalid syntax**********************************************************************1 items had failures: 1 of 1 in 1setup.average_rainfall***Test Failed*** 1 failures.

6.7

FirstTestWheredowegetourfirstrealtest?

Hint:Readthedocs:

6.8

ConvertToAppropriateDoctestForasystemtest,we'dneedtousesubprocessetc.

Butwecanjusttestourunit!average_rainfall(input_list)Butittakesalistnotastringasinput!

'2 3 4 67 -999'==>[2, 3, 4, 67, -999]Wehadtomassagetheinputtogetourtest!

6.9

TestedAverage_rainfallV2def average_rainfall(input_list): """>>> average_rainfall([2,3,4,67, -999]) 19.0 """ # Here is where your code should go return "Your computed average as a integer" #<-- change this!

$ python 1setup.py Your computed average as a integer

6.10

SecondTestRun$ python -m doctest 2firstfull.py **********************************************************************File "/Users/bparsia/Documents/2018/Teaching/COMP61511/labs/lab1/followup/2firstfull.py", line 2, in 2firstfull.average_rainfallFailed example: average_rainfall([2,3,4,67, -999])Expected: 19.0Got: 'Your computed average as a integer'**********************************************************************1 items had failures: 1 of 1 in 2firstfull.average_rainfall***Test Failed*** 1 failures.

6.11

Yay!Wehavearealandreasonabletest!

AndaclearformatforsubsequenttestsAndaninfrastructurethatmakesiteasytoruntests

WehaveabrokenimplementationAswitnessedbyatest!

WeCanFixIt!

6.12

RosieSez

6.13

FirstImplementationdef average_rainfall(input_list): """>>> average_rainfall([2,3,4,67, -999]) 19.0 """ # Here is where your code should go return sum(input_list)/len(input_list)

Willthisfailthistest?Isthereatestthatitwillpass?

6.14

FirstImplementationWithTestdef average_rainfall(input_list): """>>> average_rainfall([2,3,4,67, -999]) 19.0 >>> average_rainfall([2,3,4,67]) 19.0 """ # Here is where your code should go return sum(input_list)/len(input_list)

6.15

ThirdTestRun$ python -m doctest 4firstimpl2.py **********************************************************************File "/Users/bparsia/Documents/2018/Teaching/COMP61511/labs/lab1/followup/4firstimpl2.py", line 2, in 4firstimpl2.average_rainfallFailed example: average_rainfall([2,3,4,67, -999])Expected: 19.0Got: -184.6**********************************************************************1 items had failures: 1 of 2 in 4firstimpl2.average_rainfall***Test Failed*** 1 failures.

6.16

SecondImplementationdef average_rainfall(input_list): """>>> average_rainfall([2,3,4,67, -999]) 19.0 >>> average_rainfall([2,3,4,67]) 19.0 """ # Here is where your code should go return sum(input_list[:-1])/len(input_list[:-1])

Fixesonetestbutnottheother!Testsworktogether

6.17

ThirdImplementationdef average_rainfall(input_list): """>>> average_rainfall([2, 3, 4, 67, -999]) 19.0 >>> average_rainfall([2, 3, 4, 67]) 19.0 """ rainfall_sum = 0 count = 0 for i in input_list: if i == -999: break else: rainfall_sum += i count += 1 # Here is where your code should go return rainfall_sum/count

6.18

FourthTestRun$ python -m doctest 5secondimpl.py **********************************************************************File "/Users/bparsia/Documents/2018/Teaching/COMP61511/labs/lab1/followup/5secondimpl.py", line 2, in 5secondimpl.average_rainfallFailed example: average_rainfall([2,3,4,67, -999])Expected: 19.0Got: 19.0**********************************************************************1 items had failures: 1 of 2 in 5secondimpl.average_rainfall***Test Failed*** 1 failures.

Whaaaaaaaaaaaaaaaaaat?!

6.19

ABug!Therewasabuginourtests

Allalong!

vs.

Earliertestsfailedfortworeasons!

Onebugconcealedtheother!!!

def average_rainfall(input_list):""">>> average_rainfall([2, 3, 4, 67, -999])19.0

def average_rainfall(input_list):""" >>> average_rainfall([2, 3, 4, 67, -999])19.0

6.20

Yay!$ python -m doctest 6secondimpl2.py $$ python -m doctest -v 6secondimpl2.py Trying: average_rainfall([2,3,4,67, -999])Expecting: 19.0okTrying: average_rainfall([2,3,4,67])Expecting: 19.0ok1 items had no tests: 6secondimpl21 items passed all tests: 2 tests in 6secondimpl2.average_rainfall2 tests in 2 items.2 passed and 0 failed.Test passed.

6.21

NextTests?Thesetestsclearlyaren'tenoughWhatnext?

Lookforboundaryconditions([-999])Lookfor"oddequivalents"

Is[-999, 1]thesameas[-999]?Howabout[]and[-999]?Howabout[-999]and[-999, 0]

Lookfornormalcasesyouhaven'tcovered[-1 0 10]Foreachnewfeatureiteratetheearliermoves!

e.g.,is[-1 -2 -3 -999 1]thesameas[]?

7.2

AClassificationOfTestsBasedona5W+Happroachby (archived)Who(Programmervs.customervs.managervs...)What(Correctnessvs.Performancevs.Useabilityvs...)When(Beforewritingcodeorafter)

Orevenbeforearchitecting!Where(Unitvs.Componentvs.Integrationvs.System)

Orlabvs.fieldWhy(Verificationvs.specificationvs.design)How(Manualvs.automated)

Ondemandvs.continuous

RaySinnema

7.3

Who?Sinnema:Testsgiveconfidenceinthesystem

I.e.,theyareevidenceofaqualityWhoisgettingtheevidence?Users?Testsfocusonexternalqualities

CanIacceptthissoftware?Programmers?Testsfocusoninternalqualities

CanIcheckinthiscode?Managers?Both?

ArewereadytoreleaseButalso,whoiswritingthetest?

Abugreportisa(typicallypartial)testcase!

7.4

What?WhichqualitiesamItryingtoshow?

Internalvs.externalFunctionalvs.non-functional?Mostdevelopertestingisfunctional(i.e.,correctness)

AndattheunitlevelDoesthisclassbehaveasdesigned

7.5

When?Whenisthetestwritten?Beforethecodeiswritten?Afterthecodeiswritten?

PerhapsabetterdistinctionTestswrittenwithexistingcode/designinmindTestwrittenwithoutregardforexistingcode/designThisisrelatedtowhitevs.blackboxtesting

MaindifferenceiswhetheryourespecttheexistingAPI

7.6

Where?Unit

Smallest"chunk"ofcoherentcodeMethod,routine,sometimesaclass

:"theexecutionofacompleteclass,routine,orsmallprogramthathasbeenwrittenbyasingleprogrammerorteamofprogrammers,whichistestedinisolationfromthemorecompletesystem"

Component(McConnellspecific,Ithink)"workofmultipleprogrammersorprogrammingteams"andinisolation

McConnell

7.7

Where?(Ctnd)Integration

Testingtheinteractionoftwoormoreunits/componentsSystem

TestingthesystemasawholeInthelab

I.e.,inacontrolledsettingInthefield

I.e.,in"natural",uncontrolledsettings

7.8

Where?(CtndEncore)Regression

AbitofafunnyoneBackwardlookingandchangeoriented

Ensureachangehasn'tbrokenanythingEsppreviousfixes.

7.9

Why?Threebigreasons1. Verification(orvalidation)

Doesthesystempossessaqualitytoacertaindegree?2. Design

ImposeconstraintsonthedesignspaceBothstructureandfunction

3. ComprehensionHowdoesthesystemwork?

ReverseengineeringHowdoIworkwiththesystem?

7.10

How?Manual

TypicallyinteractiveHumanintervationformorethaninitiation

ExpectationsflexibleAutomated

ThetestexecutesandevaluatesoninitiationAutomaticallyrun(i.e.,continuously)

8.2

Bill Sempf@sempf

QA Engineer walks into a bar. Orders a beer. Orders 0 beers. Orders 999999999 beers. Orders a lizard. Orders -1 beers. Orders a sfdeljknesv.6:56 PM - Sep 23, 2014

21.1K 29.9K people are talking about this

Coverage

Esp.forfinegrainedtests,generalityisaproblemWewantasetofteststhat

determinessomepropertyatareasonablelevelofconfidence

Thistypicallyrequirescoverage

8.3

CoverageAndRequirementsConsideracceptancetesting

ForatestsuitetosupportacceptanceItneedstoprovideinformationaboutallthecriticalrequirements

ConsidertestdrivendevelopmentWheretestsdrivedesignWhathappenswithoutrequirementscoverage?

8.4

CodeCoverageAtestcase(orsuite)coversalineofcode

iftherunningofthetestexecutestheLOCCodecoverageisaminimalsortofcompleteness

SeeMcConnellon"basis"testingAimforminimaltestsuitewithfullcodecoverage

SeeTrickybittypicallyinvolvesbranches

Themorebranches,thehardertoachievecodecoverage

coverage.py

8.5

InputCoverageInputspacesare(typically)toolargetocoverdirectly

SoweneedasamplePuresampleprobablyinadequate

SpacetoolargeanduninterestingWewantabiasedsample

E.g.,wherethebugsareHence,attentiontoboundarycases

E.g.,commoninputsThatis,what'slikelytobeseen

8.6

Situation/ScenarioCoverageInputsaren'teverything

MachineconfigurationHistoryofuseInteractionpatterns

FieldtestinghelpsHencealphaplusnarrowandwidebetatesting

Systemtestsanswertothis!

8.7

LimitsOf(Developer)TestingTestingalwayshaslimits

TestsarewrongTestsarebuggyTestsareincomplete

"Self"Testingsubjecttocognitivebiases:Weinterpretwrongly

:Weinfluenceotherstointerpretincorrectly

:Welookinthewrongplace

ConfirmationbiasObserver-expectancyeffect/Experimenterbias

Congruencebias

8.8

DevelopingTestStrategiesHaveone!Howeverpreliminary

AdhoctestingrarelyworksoutwellReviewitregularly

YoumayneedadjustementsbasedonIndividualorteampsychologySituation

TheMcConnell (22.2)isagooddefaultbasicstrategy

8.9

DeveloperTestStrategiesMcConnell:22.2RecommendedApproachtoDeveloperTesting

"Testforeachrelevantrequirementtomakesurethattherequirementshavebeenimplemented.""Testforeachrelevantdesignconcerntomakesurethatthedesignhasbeenimplemented...asearlyaspossible""Use"basistesting"...Ataminimum,youshouldtesteverylineofcode.""Useachecklistofthekindsoferrorsyou'vemadeontheprojecttodateorhavemadeonpreviousprojects."Designthetestcasesalongwiththeproduct.

8.10

WhatAboutInputCoverageInWC?ByreverseengineeringwcweaimforanalternativepythonimplementationWithaclearspecaccordingtoCW1Howcanweachievefunctionalcorrectnessofminiwc?

Byachieving100%inputcoveragetosatisfythespecificationLet'sseesomeexamples...

8.11

EmptyTextFile

8.12

CommonCase:1Line

8.13

CommonCase:2Lines

8.14

VisualisingPotentialErrorsGuardagainstprograminput

Whatkindoffile?Differenttypes,wrongnames...Contentsoffile?

ProvideinputcoverageforeveryoutputdimensionNumberoflines(single,multiple)Numberofcharacters(commoncase,large,small)Numberofwords(howarewordscounted?)Numberofbytes(encoding?)

9.2

CourseworkActivitiesReadingQ1

MostlyrelatedtoreadingMostly"Recall"...withsomeinterpretation

TheywillgohigherontheBloomtaxonomy!SE1

ReadingandanalysingCW1ReverseengineeredaspecificationReengineeredminiwcfromthespec

Programconstruction

9.3

ANoteOnMarksUKmarksrunfrom0-100%

<=49=Failing(<40%seriousfailure)50-59=Pass60-69=Meritover70=DistinctionNOTETHEWIDEBANDATTHETOPANDBOTTOM

A65%isagoodmarkAn85%isexceedinglyrareOver70%isfairlyrare

9.4

Q1Meanof3.57(71%)

Lastyear:3.71(74%)Wewilldosome"inexamconditions"Let'sdelve

9.5

SimplifiedProblemThiswasasmallproblem

WithclearboundariesEvenhere:

WeendedupwithsupportprogramsAndcornerscut

Softwareengineeringis(complex)systemengineeringOnboththeproductandprojectsidesWeuseacomplexinfrastructure!

9.6

ChallengesWhatwerethechallengesyouencountered?

Whatchallengeswereinherenttotheproblem?

Whatchallengeswereenvironmental?

9.7

CW1MarksInBlackboard!Average:4/10(40%)Max:8>70%660%s1450%s5<49%26

Notunusualforfirstassignment!LearningcurveFinalcourseworkaveagetendstobe≈62%

9.8

CW1FeedbackFeedbackisinBlackboardFeedbackisdetailedbutabstract

There seems to be a miniwc.py: 0.5/0.5 points. There seems to be a doctest_miniwc.py and test files: 0.5/0.5 points. Prohibited libraries have been used: 0/1 points. Formatting was correct: 1/1 points.The script passed 35.7% of miniwc simple tests: 2/5 points. The script passed 0.0% of miniwc binary tests: 0/1 points. The script passed 0.0% of miniwc unicode tests: 0/1 points. Penalties: none. Total marks: 4.0/10.0

9.9

NextWe'regoingtodomorewc

Job1istofixyourminiwc.py(nowcalledwc.py)Fixyourtests!Addmoretests!

Unicode!Binary!Job2istoaddnewfunctionality

Flags!MultipleinputfilesJob3istoupdateyourtests

NotethatJob3isn't*temporallylast!Youwilldoacodereviewofminiwc.pyDiscussthefeedback!Thisistheonlytimeyoucantalkwithaclassmateaboutit!

9.10

OtherCourseworkSE2!

YouneedtoreadNoSilverBulletSE1!

TAsareavailabletodiscuss

top related